Skip to content

Files

Latest commit

bbbfef8 · Sep 26, 2018

History

History

047.Permutations II

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Sep 24, 2018
Sep 23, 2018
Sep 26, 2018

全排列 II

题目描述

给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

解法

解法①:

将数组的首元素依次与数组的每个元素交换(两元素不相等才进行交换),对于每一轮交换,对后面的数组进行递归调用。当元素只剩下一个时,添加此时的数组到 list 中。

注意:第 i 个数字与第 j 个数字交换时,要求[i, j) 中没有与第 j 个数字相等的数。

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        permute(list, nums, 0);
        return list;
    }
    
    private void permute(List<List<Integer>> list, int[] nums, int start) {
        int end = nums.length - 1;
        if (start == end) {
            List<Integer> tmp = new ArrayList<>();
            for (int val : nums) {
                tmp.add(val);
            }
            
            list.add(tmp);
            
        }
        
        for (int i = start; i <= end; ++i) {
            if (isSwap(nums, start, i)) {
                swap(nums, i, start);
                permute(list, nums, start + 1);
                swap(nums, i, start);
            }
            
        }
        
    }
    
    private boolean isSwap(int[] nums, int from, int to) {
        for (int i = from; i < to; ++i) {
            if (nums[i] == nums[to]) {
                // [from, to) 中出现与 第 to 个数相等的数,返回 false,不进行交换和全排列操作
                return false;
            }
        }
        return true;
    }
    
    private void swap(int[] nums, int i, int j) {
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}

解法②:

利用空间换取时间,减少 n^2 复杂度。这里的空间,可以采用数组,或者 HashMap。

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        permute(list, nums, 0);
        return list;
    }
    
    private void permute(List<List<Integer>> list, int[] nums, int start) {
        int end = nums.length - 1;
        if (start == end) {
            List<Integer> tmp = new ArrayList<>();
            for (int val : nums) {
                tmp.add(val);
            }
            
            list.add(tmp);
            
        }
        
        
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i <= end; ++i) {
            max = Math.max(max, nums[i]);
            min = Math.min(min, nums[i]);
        }
        // 空间换取时间
        boolean[] flag = new boolean[max - min + 1];
        
        for (int i = start; i <= end; ++i) {
            int index = nums[i] - min;
            if (!flag[index]) {
                // 说明当前的数没在之前出现过
                swap(nums, i, start);
                permute(list, nums, start + 1);
                swap(nums, i, start);
                
                // 标志位设为 true
                flag[index] = true;
            }
        }
        
    }
    
    private void swap(int[] nums, int i, int j) {
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}

解法➂:

这是非递归的解法。思路就是:

  • 先保存升序数组;
  • 找到下一个比该数组大的数组,保存;
  • 直到数组呈降序。

如何找到下一个数组呢?从后往前,找到第一个升序状态的位置,记为 i-1,在[i, length - 1] 中找到比 nums[i - 1] 大的,且差值最小的元素(如果有多个差值最小且相同的元素,取后者),进行交换。将后面的数组序列升序排列,保存。然后恢复 i,继续循环。

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        // 先排序,并添加至 list 中
        Arrays.sort(nums);
        addList(list, nums);
        permute(list, nums);
        return list;
    }
    
    private void permute(List<List<Integer>> list, int[] nums) {
        int n = nums.length;
        for (int i = n - 1; i > 0; --i) {
            if (nums[i - 1] < nums[i]) {
                // 在数组右边找到比 nums[i - 1] 大的,但差值最小的数的下标
                // 若有多个最小差值,取后者
                int index = findMinIndex(nums, i, nums[i - 1]);
                swap(nums, i - 1, index);
                sortArr(nums, i);
                addList(list, nums);
                i = n;
            }
        }
        
    }
    
    private void sortArr(int[] nums, int start) {
        int n = nums.length - 1;
        while (start < n) {
            swap(nums, start, n);
            ++start;
            --n;
        }
    }
    
    private int findMinIndex(int[] nums, int start, int val) {
        int index = start;
        int distance = nums[start] - val;
        for (int i = start; i < nums.length; ++i) {
            if (nums[i] > val && (nums[i] - val <= distance)) {
                distance = nums[i] - val;
                index = i;
            }
        }
        return index;
    }
    
    private void addList(List<List<Integer>> list, int[] nums) {
        List<Integer> tmpList = new ArrayList<>();
        for (int val : nums) {
            tmpList.add(val);
        }
        list.add(tmpList);
        
    }
    
    private void swap(int[] nums, int i, int j) {
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}