Permutations
Given a collection of numbers, return all possible permutations.
For example,
[1,2,3] have the following permutations:
[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].
Solution1
- 典型的回溯问题,回溯问题一般说来用递归方法求解会比较简单
public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
HashSet<Integer> set = new HashSet<Integer>();
permute(nums,set,new ArrayList<Integer>(),result);
return result;
}
public void permute(int[] nums,HashSet<Integer> set,List<Integer> item,List<List<Integer>> result){
if(item.size()==nums.length){
result.add(item);
return;
}
for(int i=0;i<nums.length;i++){
if(set.contains(nums[i])) continue;
List<Integer> temp = new ArrayList<Integer>(item);
temp.add(nums[i]);
HashSet<Integer> newSet = new HashSet<Integer>(set);
newSet.add(nums[i]);
permute(nums,newSet,temp,result);
}
}
}
Solution2
- 用迭代的方法来实现上述回溯过程如下:
public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(nums.length==0) return result;
Stack<Integer> stack = new Stack<Integer>();
HashSet<Integer> set = new HashSet<Integer>();
List<Integer> item = new ArrayList<Integer>();
while(true){
for(int i=0;i<nums.length;i++){//往下递归的过程
if(set.contains(i)) continue;
stack.push(i);
set.add(i);
item.add(nums[i]);
break;
}
if(item.size()==nums.length){
result.add(new ArrayList<Integer>(item));
item.remove(item.size()-1);
set.remove(stack.pop());
int j;
for(j = nums.length;j>=nums.length;){//实现回溯过程
if(stack.empty()) return result;
j = stack.pop();
set.remove(j);
item.remove(item.size()-1);
for(j++;j<nums.length&&set.contains(j);j++);
}
stack.push(j);
set.add(j);
item.add(nums[j]);
}
}
}
}
- 从上可以看出,一般说来,用迭代的方法来实现递归,都要借用到栈来保存当前的数组下标,在合适的时候回溯时,要在这个下标的地方往下递归,所以这里使用到栈来保存下标,但是可以看到迭代方法比较复杂。
Solution3
- 另一种类型的递归,更加trick一些
public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
permute3(nums,0,result);
return result;
}
public void permute3(int[] nums,int start,List<List<Integer>> result){
if(start==nums.length){
List<Integer> item = new ArrayList<Integer>();
for(int num:nums) item.add(num);
result.add(item);
return;
}
for(int i=start;i<nums.length;i++){
int temp = nums[start];
nums[start] = nums[i];
nums[i] = temp;
permute3(nums,start+1,result);
nums[i] = nums[start];
nums[start] = temp;
}
}
}
Solution4
- 这里实际可以用动态规划的方法来求解。基本思路如下:当数组为[1]时,则答案为[1];当数组为[1,2]时,则答案为[1,2]和[2,1];当数组为[1,2,3]时,则答案可以在[1,2]和[2,1]的基础上构成。即往这两个数组中的不同位置插入3即可。如此得到下面的解法
import java.util.List;
import java.util.ArrayList;
public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(nums.length==0) return result;
List<Integer> temp = new ArrayList<Integer>();
temp.add(nums[0]);
result.add(temp);
for(int i=1;i<nums.length;i++){
int size = result.size();
for(int j=0;j<size;j++){
temp = result.get(j);
for(int k=0;k<=temp.size();k++){//这里便是各个位置插入的过程
ArrayList<Integer> temp1 = new ArrayList<Integer>(temp);
temp1.add(k,nums[i]);
result.add(temp1);
}
}
while(size-->0) result.remove(0);
}
return result;
}
}
Solution5
- 这里借鉴 Leetcode31 Next Permutation的思路来求解本题。
public class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
Arrays.sort(nums);
while(true){
List<Integer> item = new ArrayList<Integer>();
for(int num:nums) item.add(num);
result.add(item);
int i,j;
for(i=nums.length-1;i>0;i--) if(nums[i-1]<nums[i]) break;
if(i<=0) break;
for(j=nums.length-1;j>0;j--) if(nums[j]>nums[i-1]) break;
int temp = nums[j];
nums[j] = nums[i-1];
nums[i-1] = temp;
for(j = nums.length-1;i<j;i++,j--){
temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
}
return result;
}
}
- 思路清晰易懂,并且还复用性还非常高。不管是有重复数字还是没有重复数字都可以用这种方法求解。所以比回溯法更值得推荐。