排列问题—46. Permutations && 47. Permutations II
一、不包含重复元素的集合,列出所有排列组合
1. 题目
46. Permutations
Given a collection of distinct integers, return all possible permutations.
Example:
Input: [1,2,3]
Output:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
2. 题目分析
这个也是高中学的,找出一个大的集合的所有排列组合。前面已经分析子集问题,可以参考子集问题—78. Subsets && 90. Subsets II
排列组合问题与子集问题的区别在于,子集具有无序性,即{1,2}和{2,1}是一样的,但在排列组合种,这两个集合是不相等的。
3. 解题思路
排列组合与求子集的思路是一样的,都是使用回溯算法,但不同之处是,每次递归的循环条件是,每次回溯到第一个元素,然后依次遍历添加元素,但此时需要判断当前元素是否已经添加到中间结果集中,如果添加了,则无需再加入。
4. 代码实现(java)
package com.algorithm.leetcode.backtracking;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 凌 on 2019/2/2.
* 注释:46. Permutations
*/
public class Permute {
public static void main(String[] args) {
Permute permute= new Permute();
int[] nums={1,2,3};
List<List<Integer>> result = permute.permute(nums);
System.out.println(result.toArray());
}
/**
* 全排列不包括重复元素
* @param nums
* @return
*/
public List<List<Integer>> permute(int[] nums) {
if (nums == null){
return new ArrayList<>();
}
List<List<Integer>> result = new ArrayList<>();
List<Integer> set = new ArrayList<Integer>();
int start = 0;
dfs(nums,result,set);
return result;
}
public void dfs(int[] nums,List<List<Integer>> result, List<Integer> set){
if (set.size() == nums.length){
result.add(new ArrayList<Integer>(set));
return;
}
for (int i = 0; i < nums.length; i++) {
if (!set.contains(nums[i])){
set.add(nums[i]);
dfs(nums,result,set);
set.remove(set.size()-1);
}
}
}
}
二、包含重复元素的集合,列出所有排列组合
1. 题目
47. Permutations
Given a collection of numbers that might contain duplicates, return all possible unique permutations.
Example:
Input: [1,1,2]
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
2. 题目分析
也是给定集合,进行求所有排列组合。不同的是集合有重复元素,
3. 解题思路
这里采用了递归的思路。避免重复的核心思路在于,使用一个boolean数组来代表当前的数值是否已经被使用过。当前的值如果已经被使用过,则继续判断下一个数值。如果当前的值为重复值,则只要前面的值没有被使用过,则当前值就不可以被使用。这样确保了只有第一个出现的重复值可以算进结果集,后序重复的情况不会被添加进结果集。
例如,假设输入的数组为[1,1,2]。则当第一个1被添加进结果集时,可以继续使用第二个1作为元素添加进结果集从而生成112。同理,当试图将第二个1作为第一个元素添加进结果集时,只要第一个1还没有被使用过,则不可以使用第二个1。因此,112不会被重复的添加进结果集。
其实,这个算法保证了所有重复的数字在结果集中的顺序和在原输入数组中的顺序是相同的。假设将[1,1,2]表示为[1,1#,2],那么结果集中会确保1永远在数值1#的前面,从而避免了11#2和1#12的重复情况出现。
4. 代码实现(java)
package com.algorithm.leetcode.backtracking;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by 凌 on 2019/2/5.
* 注释:47. Permutations II
*/
public class PermuteUnique {
/**
* 求全排列,包括重复元素
* @param nums
* @return
*/
public List<List<Integer>> permuteUnique(int[] nums) {
if (nums == null){
return new ArrayList<>();
}
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
List<Integer> set = new ArrayList<Integer>();
boolean[] isVisited = new boolean[nums.length];
dfs(nums,result,set,isVisited);
return result;
}
/**
* 使用isVisited数组表示某个元素是否已经被访问
* @param nums
* @param result
* @param set
* @param isVisited
*/
public void dfs(int[] nums,List<List<Integer>> result, List<Integer> set,boolean[] isVisited){
if (set.size() == nums.length){
result.add(new ArrayList<Integer>(set));
return;
}
for (int i = 0; i < nums.length; i++) {
if (isVisited[i]) {
continue;
}
if (i > 0 && nums[i] == nums[i - 1] && !isVisited[i - 1]) {
continue;
}
isVisited[i] = true;
set.add(nums[i]);
dfs(nums, result, set, isVisited);
isVisited[i] = false;
set.remove(set.size() - 1);
}
}
}