如何思考剪枝过程?

  • 数组中有重复数字会导致出现重复的排列,因为要对原数组循环遍历每个数字来递归获取排列数。
  • 要阻止出现重复排列的情况,就要在同一层循环中不使用重复的数字来递归,为了方便判断是否为重复的数字,可以先对数组进行排序,再判断相邻的两个数字是否重复即可。
  • used数组用于判断某个数字是否被使用过,如果被使用过则跳过这个数字避免重复使用。但在重复数字的场景下,如果数字没被使用过还需要判断他是否是跟前一个数字重复,如果重复了也要跳过这个数字。
  • used数组只在递归过程会逐个变true,如果used[i-1]为false说明和当前数字相同的上一个数字在上一次循环中被递归使用了,回溯后当前层为false,所以如果当前数字和前一个数字相同,就不能再选择这个数字进入递归。
  • 因为要判断两个相邻的数字,所以起码要从第二个数字开始判断,即i>0。

参考

https://blog.nowcoder.net/n/50274e5a9c4142be9fa551fba14b6f6a?f=comment

/**
 * 
 * @param num int整型一维数组 
 * @return int整型二维数组
 */
function permuteUnique( num ) {
    const ret = []
    const used = Array.from(num, (item) => item = false);  // 用于判断数组中的元素是否使用过,如果使用过则跳过当前元素
    num.sort();  // 为了能方便的判断两个相邻数字是否相同,需要先对数组进行排序
    // write code here
    function dfs(num, ansArray=[]) {
        if (ansArray.length === num.length) {
            ret.push(ansArray.slice());  // 要传入一个副本,也可以用[...ansArray]
            return;
        }
        for (let i = 0; i < num.length; ++i) {
            // 有重复数字后会导致出现重复的排列,要阻止这种情况则必须要同一层中重复数字不能重复递归使用,关键是如何判断同一层重复数字的逻辑。
            // used数组只在递归过程会逐个变true,如果当前为true上一个为false说明这是同一层循环中的上一次循环回溯后变false了,此时两者在同一层。
			// 因为要判断两个相邻的数字,所以起码要从第二个数字开始判断,即i>0
            if (used[i] || (i > 0 && num[i-1] === num[i] && !used[i-1])) {  
                continue;
            }
            
            ansArray.push(num[i]);
            used[i] = true;
            dfs(num, ansArray);  // 递归传入的是原数组,通过判断原数组的元素是否使用过来缩减元素范围,而不是通过传入削减后的数组切片,这样会丢失元素
            used[i] = false;
            ansArray.pop();
        }
    } 

    dfs(num, []);

    return ret;
}
module.exports = {
    permuteUnique : permuteUnique
};
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param num int整型一维数组 
     * @return int整型ArrayList<ArrayList<>>
     */
    private ArrayList<ArrayList<Integer>> res = new ArrayList<>();
    public ArrayList<ArrayList<Integer>> permuteUnique (int[] num) {
        // write code here
        boolean[] used = new boolean[num.length];
        ArrayList<Integer> arr = new ArrayList<Integer>();
        Arrays.sort(num);
        dfs(num, arr, used);

        return this.res;
    }

    private void dfs(int[] num, ArrayList<Integer> arr, boolean[] used) {
        if (num.length == arr.size()) {
            this.res.add(new ArrayList<Integer>(arr));
            return;
        }

        for (int i = 0; i < num.length; ++i) {
            if (used[i] || (i > 0 && num[i] == num[i-1] && used[i-1] == false)) {
                continue;
            }

            arr.add(num[i]);
            used[i] = true;
            dfs(num, arr, used);
            used[i] = false;
            arr.remove(arr.size()-1);
        }

    }
}