【思路-Java、Python】回溯算法|递归实现
全排列是将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个。现以{1, 2, 3, 4, 5}为例说明如何编写全排列的递归算法。
1、首先看最后两个数4, 5。 它们的全排列为4 5和5 4, 即以4开头的5的全排列和以5开头的4的全排列。由于一个数的全排列就是其本身,从而得到以上结果。
2、再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合。
从而可以推断,设一组数p = {r1, r2, r3, ... ,rn}, 全排列为perm(p),pn = p - {rn}。因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。当n = 1时perm(p} = r1。为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。
public class Solution {
public List> permute(int[] nums) {
List> res = new ArrayList>();
ArrayList temp = new ArrayList();
dfs(res, nums, 0);
return res;
}
private void dfs(List> res, int[] nums, int j) {
if (j == nums.length) {
List temp = new ArrayList();
for (int num : nums) temp.add(num);
res.add(temp);
}
for (int i = j; i < nums.length; i++) {
swap(nums, i, j);
dfs(res, nums, j+1);
swap(nums, i, j);
}
}
private void swap(int[] nums, int m, int n) {
int temp = nums[m];
nums[m] = nums[n];
nums[n] = temp;
}
}
25 / 25 test cases passed. Runtime: 2 ms Your runtime beats 91.90% of javasubmissions.
class Solution(object):
def permute(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res=[]
def helper(res,l,r,n,max):
if n==max:
print l
res.append(l)
for i in range(0,len(r)):
helper(res,l+[r[i]],r[:i]+r[i+1:],n+1,max)
helper(res,[],nums,0,len(nums))
return res
25 / 25
test cases passed. Runtime: 80 ms Your runtime beats 77.22% of pythonsubmissions.
【思路2-Java】回溯算法|递归实现
这种思路和思路1的区别是:思路1采用位置两两交换,交换后出现一种新的组合,将这种新的组合添加到中间集,再添加到结果集中。而这种思路是采用逐一向中间集添加元素,并将当中间集元素个数等于 nums 长度的时候,将中间集添加到结果集中,并终止该层递归:
public class Solution {
public List> permute(int[] nums) {
List> res = new ArrayList<>();
dfs(res, new ArrayList(), nums, new boolean[nums.length]);
return res;
}
private void dfs(List> res, List temp, int[] nums, boolean[] used) {
if(temp.size() == nums.length) {
res.add(new ArrayList<>(temp));
return;
}
for(int i = 0; i < nums.length; i++) {
if(!used[i]) {
used[i] = true;
temp.add(nums[i]);
dfs(res, temp, nums, used);
temp.remove(temp.size() - 1);
used[i] = false;
}
}
}
}
25 / 25
test cases passed. Runtime: 5 ms Your runtime beats 23.82% of javasubmissions.
【思路3-Java】非递归实现
提供非递归实现的思路,假设我们有了当前前 i 个元素的组合,当第 i+1个元素加入时,我们需要做的是将这个元素加入之前的每一个结果,并且放在每个结果的每个位置,因为之前的结果没有重复,所以加入新元素的结果也不会有重复(这里是假定数字集合没有重复),这种思路代码如下:
public class Solution {
public List> permute(int[] nums) {
List> res = new ArrayList<>();
List first = new ArrayList<>();
first.add(nums[0]);
res.add(first);
for(int i = 1; i < nums.length; i++) {
List> newRes = new ArrayList<>();
for(List temp : res) {
int size = temp.size() + 1;
for(int j = 0; j < size; j++) {
List item = new ArrayList<>(temp);
item.add(j, nums[i]);
newRes.add(item);
}
}
res = newRes;
}
return res;
}
}
25 / 25
test cases passed. Runtime: 3 ms Your runtime beats 67.54% of javasubmissions.
下面是对思路3 的一种优化:
public class Solution {
public List> permute(int[] nums) {
List> res = new ArrayList>();
if (nums.length == 0 || nums == null)
return res;
List list = new ArrayList();
list.add(nums[0]); // Add the first element into the list;
res.add(list);
for (int i = 1; i < nums.length; i++) {
// Keep track of the size of current result;
int size = res.size();
for (int j = 0; j < size; j++) {
int size2 = res.get(0).size();
for (int k = 0; k <= size2; k++) {
List temp = new ArrayList<>(res.get(0));
temp.add(k, nums[i]);
res.add(temp);
}
res.remove(0);
}
}
return res;
}
}
25 / 25
test cases passed. Runtime: 4 ms Your runtime beats 35.66% of javasubmissions.