重新排序得到 2 的幂

leecode-869 2021/10/28

Posted by donlv1997 on October 28, 2021

hit count image

“leecode打卡,我还是暴力法吧”

题干

给定正整数 N ,我们按任何顺序(包括原始顺序)将数字重新排序,注意其前导数字不能为零。

如果我们可以通过上述方式得到 2 的幂,返回 true;否则,返回 false

示例

示例 1:

输入:1
输出:true

示例 2:

输入:10
输出:false

示例 3:

输入:16
输出:true

示例 4:

输入:24
输出:false

示例 5:

输入:46
输出:true

提示:

  1. $1 <= N <= 10^9$

题解

没有太多需要特别注意的,可以说下面两点:

位运算

计算一个数$n$是不是$2$的整数次幂,用了一个位运算的技巧:

(n & (n - 1)) == 0;

传参

Java的函数入参,默认传引用,不是C++中的拷贝传递,因此需要这样传递:

dfs(new ArrayList<>(list), tempList, result);

代码

public class Main {

    public static boolean reorderedPowerOf2(int n) {
        List<Integer> numList = getNumList(n);
        List<Integer> result = new ArrayList<>();
        dfs(numList, new LinkedList<>(), result);
        for (Integer integer : result) {
            if (isPowerOf2(integer)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isPowerOf2(int n) {
        return (n & (n - 1)) == 0;
    }

    private static List<Integer> getNumList(int n) {
        List<Integer> list = new ArrayList<>();
        while (n > 0) {
            list.add(n % 10);
            n /= 10;
        }
        return list;
    }

    private static void dfs(List<Integer> list, LinkedList<Integer> tempList, List<Integer> result) {
        // add this result to result list
        if (list.isEmpty()) {
            int sum = 0;
            for (Integer integer : tempList) {
                sum = sum * 10 + integer;
            }
            result.add(sum);
            return;
        }
        // backtrack
        for (int i = 0; i < list.size(); i++) {
            if (tempList.isEmpty() && list.get(i) == 0) {
                continue;
            }
            tempList.add(list.remove(i));
            dfs(new ArrayList<>(list), tempList, result);
            list.add(i, tempList.removeLast());
        }
    }

    public static void main(String[] args) {
        System.out.println(reorderedPowerOf2(46));
    }

}

官方题解(哈希字典法)

这个解法我觉得特异性太强了,泛用性不强,但是看一眼无妨

由于我们可以按任何顺序将数字重新排序,因此对于两个不同的整数 $a$ 和 $b$ ,如果其十进制表示的字符数组,从小到大排序后的结果是相同的,那么若 $a$ 能够重排得到 $2$ 的幂,$b$ 也可以;若 $a$ 不能重排得到 $2$ 的幂,那么 $b$ 也不能。

进一步地,只要 $a$ 和 $b$ 的十进制表示的字符数组中,从 $0$ 到 $9$ 每个字符的出现次数,在 $a$ 和 $b$ 中都是一样的,那么 $a$ 和 $b$ 能否重排得到 $2$ 的幂的结果是一样的。

由于 $2^{29} < 10^9 < 2^{30}$ ,因此在 $[1,10^9]$ 范围内有 $2^0,2^1,\cdots,2^{29}$ 一共 $30$ 个 $2$ 的幂。对这 $30$ 个数的每个数,我们可以预处理其十进制表示的字符数组中从 $0$ 到 $9$ 每个字符的出现次数,记在一个长度为 $10$ 的数组中,并用一哈希表记录这些数组。对于数字 $n$ ,我们同样统计其十进制表示的字符数组中从 $0$ 到 $9$ 每个字符的出现次数,然后去哈希表中查找,若存在则说明 $n$ 可以通过重排得到 $2$ 的幂,否则不能。

class Solution {
    Set<String> powerOf2Digits = new HashSet<String>();

    public boolean reorderedPowerOf2(int n) {
        init();
        return powerOf2Digits.contains(countDigits(n));
    }

    public void init() {
        for (int n = 1; n <= 1e9; n <<= 1) {
            powerOf2Digits.add(countDigits(n));
        }
    }

    public String countDigits(int n) {
        char[] cnt = new char[10];
        while (n > 0) {
            ++cnt[n % 10];
            n /= 10;
        }
        return new String(cnt);
    }
}

// 作者:LeetCode-Solution
// 链接:https://leetcode-cn.com/problems/reordered-power-of-2/solution/zhong-xin-pai-xu-de-dao-2-de-mi-by-leetc-4fvs/
// 来源:力扣(LeetCode)

补充

track列表使用链表实现

链表移除尾部元素需要 $o(1)$ 复杂度,如果是数组则需要 $o(n)$ 复杂度

全排列写法要点

如果一个元素已经被选择过,不应该直接将该元素移除,而是应该建一个布尔数组boolean[] marked,对应的索引设为true

如果不想递归函数的入参太多,可以改成使用私有变量

public class Main2 {

    /**
     * n每一位构成的list
     */
    private List<Integer> numList;

    /**
     * track
     */
    private LinkedList<Integer> track;

    /**
     * result list
     */
    private List<Integer> result;

    /**
     * numList中第i个数有没有被选中
     */
    private boolean[] marked;

    public boolean reorderedPowerOf2(int n) {
        numList = getNumList(n);
        track = new LinkedList<>();
        result = new ArrayList<>();
        marked = new boolean[numList.size()];
        backtrack();
        for (int integer : result) {
            if (isPowerOf2(integer)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isPowerOf2(int n) {
        return (n & (n - 1)) == 0;
    }

    private static List<Integer> getNumList(int n) {
        List<Integer> list = new ArrayList<>();
        while (n > 0) {
            list.add(n % 10);
            n /= 10;
        }
        return list;
    }

    private void backtrack() {
        // add this result to result list
        if (track.size() == numList.size()) {
            int sum = 0;
            for (Integer integer : track) {
                sum = sum * 10 + integer;
            }
            result.add(sum);
            return;
        }
        // backtrack
        for (int i = 0; i < numList.size(); i++) {
            if (marked[i] || (track.isEmpty() && numList.get(i) == 0)) {
                continue;
            }
            track.add(numList.get(i));
            marked[i] = true;
            backtrack();
            marked[i] = false;
            track.removeLast();
        }
    }

    public static void main(String[] args) {
        System.out.println(new Main2().reorderedPowerOf2(215));
    }

}