方法:递归+回溯
因为有重复的元素,所以需要去重,前面用到的交换的方法不适用了。这里我们采用一个临时数组记录当前添加的结果,用一个标记数组判断哪些元素已经加入,再每次添加时都需要判断是否和前一个元素相等,相等则不添加。
步骤:
- step 1:先对数组按照字典序排序,获取第一个排列情况。
- step 2:准备一个数组暂存递归过程中组装的排列情况。使用额外的visited数组用于记录哪些位置的数字被加入了。
- step 3:每次递归先判断,若临时数组长度到达原数组长度就是一种排列情况。
- step 4:从头遍历数组,获取数字加入:首先根据vis数组,已经加入的元素不能再次加入了;同时,如果当前的元素num[i]与同一层的前一个元素num[i-1]相同且num[i-1]已经用,也不需要将其纳入。
- step 5:进入下一层递归前将vis数组当前位置标记为使用过。
- step 6:回溯的时候需要修改vis数组当前位置标记,同时去掉刚刚加入数组的元素。
import java.util.*; public class Solution { public void recursion(ArrayList<ArrayList<Integer>> res, int[] num, ArrayList<Integer> temp, Boolean[] visited) { //临时数组满了,添加temp到res if (temp.size() == num.length) { res.add(new ArrayList<Integer>(temp)); return; } for (int i = 0; i < num.length; ++i) { //已经加入临时数组的不需要再加入了 if (!visited[i]) { if (i>0&& num[i] == num[i - 1]&&!visited[i-1]) { continue; } temp.add(num[i]); visited[i] = true; //标记为使用过 recursion(res, num, temp, visited); //回溯到递归前状态,当前位置未被使用 visited[i] = false; //移除最后加入的元素 temp.remove(temp.size()-1); } } } public ArrayList<ArrayList<Integer>> permuteUnique (int[] num) { //排序后num就是有序的数组了 Arrays.sort(num); //用于判断数组中某个位置是否加入了临时数组 Boolean[] visited = new Boolean[num.length]; //填充数组 Arrays.fill(visited, false); ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>(); //临时数组 ArrayList<Integer> temp = new ArrayList<Integer>(); //递归,res为最终结果数组 recursion(res, num, temp, visited); return res; } }