题目:

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].

Hide Tags

Backtracking 

链接: http://leetcode.com/problems/permutations/

题解:

求数组的全排列。需要用到DFS和Backtracking。 原理是从0到数组长度N,每次对之前加入的元素进行回溯。 注意此解法假定输入数组之中没有重复元素。

Time Complexity - O(n!), Space Complexity - O(n)。

public class Solution {
public ArrayList<ArrayList<Integer>> permute(int[] nums) {
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
if(nums == null || nums.length == 0)
return result;
Arrays.sort(nums);
ArrayList<Integer> list = new ArrayList<Integer>();
helper(result, list, nums);
return result;
} private void helper(ArrayList<ArrayList<Integer>> result, ArrayList<Integer> list, int[] nums){
if(list.size() == nums.length){ //in this problem we assume no duplicate exists in input array
result.add(new ArrayList<Integer>(list));
return;
} for(int i = 0; i < nums.length; i++){
if(list.contains(nums[i]))
continue;
list.add(nums[i]);
helper(result, list, nums);
list.remove(list.size() - 1);
}
}
}

二刷:

Java:

一般来说可以分为两种做法,一种是DFS + backtracking, 另外一种是利用next permutation一样的做法来一个一个求出next permutation。

Next permutation做法:

为了使用Arrays.asList(new Nums),我们先把int[] nums转换为Integer[] newNums。然后新建一个method -  boolean hasNextPermutation。把newNums排序以后,转换为ArrayList,加入到结果集中。接下来我们进入循环,当hasNext为true时,我们在hasNext的函数体里面已经完成了交换,把这时候的nums转换为ArrayList加入到结果集中,然后进行下一次判断。 最后返回结果。 速度比较慢。

Time Complexity - O(n!), Space Complexity - O(n)

public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) {
return res;
}
Integer[] newNums = new Integer[nums.length];
for (int i = 0; i < nums.length; i++) {
newNums[i] = nums[i];
}
Arrays.sort(newNums);
res.add(new ArrayList<Integer>(Arrays.asList(newNums)));
while (hasNextPermutation(newNums)) {
res.add(new ArrayList<Integer>(Arrays.asList(newNums)));
}
return res;
} private boolean hasNextPermutation(Integer[] newNums) {
for (int i = newNums.length - 2; i >= 0; i--) {
if (newNums[i] < newNums[i + 1]) {
for (int j = newNums.length - 1; j > i; j--) {
if (newNums[j] > newNums[i]) {
swap(newNums, i, j);
reverse(newNums, i + 1, newNums.length - 1);
return true;
}
}
}
}
return false;
} private void swap(Integer[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
} private void reverse(Integer[] nums, int i, int j) {
while (i < j) {
swap(nums, i++, j--);
}
}
}

DFS + backtracking(使用list.contains去重复):

我们也可以用DFS+Backtracking来做。这里需要建立一个helper函数permute和一个辅助List<Integer> onePerm。当onePerm.size() == nums.size()的时候,我们把这个辅助list加入到结果中。否则我们进行从0到nums.length - 1的遍历,在这个遍历过程中我们使用了DFS+回溯。我们假设给定nums中不含重复元素, 一个重要的去重步骤是,假如当前辅助List里已经有nums[i]了,那么我们进行跳过。这一步使用了O(n)的复杂度,所以导致整个过程比较慢。

Time Complexity - O(n!), Space Complexity O(n)。  (其实由于有这一步list.contains()递归复杂度应该是  (n * 1) *((n - 1) * 2) *((n - 2) * 3)...)应该等于 ∏(12*..。 *n2)

public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) {
return res;
}
Arrays.sort(nums);
List<Integer> onePerm = new ArrayList<Integer>();
permute(res, onePerm, nums);
return res;
} public void permute(List<List<Integer>> res, List<Integer> onePerm, int[] nums) {
if (onePerm.size() == nums.length) {
res.add(new ArrayList<Integer>(onePerm));
return;
}
for (int i = 0; i < nums.length; i++) {
if (onePerm.contains(nums[i])) {
continue;
}
onePerm.add(nums[i]);
permute(res, onePerm, nums);
onePerm.remove(onePerm.size() - 1);
}
}
}

DFS:

也可以用类似自底向上新建立List<Integer>的方式来跳过用List.contains来去重复的过程。

  1. 我们先传入辅助函数一个空ArrayList<>()
  2. 遍历数组,当index i <= newPerm.size()时,我们把num[pos]这个元素分别加入到这个newPerm的从0 到 newPerm.size()的不同位置中去
  3. 然后再进行下一层dfs,继续加入nums中的下一个元素
  4. 这样做的坏处是空间复杂度会比较高,每一层的每次遍历都会新开一个ArrayList,假如nums很大的话,理论上有可能会触发更多次的garbage collection。但试验了几次以后比上面的两个solution要快,先存着当做一种不同的思路了。
  5. 还有一点是这里的code没有对nums排序,好像这种方法排序与不排序并没有什么关系....

Time Complexity - O(n!), Space Complexity - O(n!)

public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) {
return res;
}
getPermutions(res, new ArrayList<Integer>(), nums, 0);
return res;
} public void getPermutions(List<List<Integer>> res, List<Integer> onePerm, int[] nums, int pos) {
if (onePerm.size() == nums.length) {
res.add(new ArrayList<Integer>(onePerm));
return;
}
for (int i = 0; i <= onePerm.size(); i++) {
List<Integer> newPerm = new ArrayList<>(onePerm);
newPerm.add(i, nums[pos]);
getPermutions(res, newPerm, nums, pos + 1);
}
}
}

三刷:

还是使用了next permutation的方法。需要再练习练习。还有一些方法,比如不断地把第i位的元素插入到 i - 1的结果中去。 或者不断递归swap start和i并且回溯,都可以完成。Discuss区的大神们写得更好。

查了一下资料,更好的方法应该是Johnson-Trotter法,根据上一个permutation的奇偶性来升序或者降序地添加新元素,也是O(n!)的时间复杂度。但这么做可能导致结果是无序的。

Java:

使用Next Permutation:

public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) return res;
Arrays.sort(nums);
do {
List<Integer> permu = new ArrayList<>();
for (int num : nums) permu.add(num);
res.add(permu);
} while (hasNextPermutation(nums));
return res;
} private boolean hasNextPermutation(int[] nums) {
int len = nums.length;
for (int i = len - 2; i >= 0; i--) {
if (nums[i] < nums[i + 1]) {
for (int j = len - 1; j > i; j--) {
if (nums[j] > nums[i]) {
swap(nums, i, j);
reverse(nums, i + 1, len - 1);
return true;
}
}
}
}
return false;
} private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
} private void reverse(int[] nums, int i, int j) {
while (i < j) swap(nums, i++, j--);
}
}

通过不断swap:  速度非常快

public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
permute(res, nums, 0);
return res;
} private void permute(List<List<Integer>> res, int[] nums, int pos) {
if (pos == nums.length) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < nums.length; i++) list.add(nums[i]);
res.add(list);
return;
} for (int i = pos; i < nums.length; i++) {
swap(nums, pos, i);
permute(res, nums, pos + 1);
swap(nums, pos, i);
}
} private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}

通过不断insert下一个元素

public class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums.length == 0) return res;
res.add(new ArrayList<>());
for (int i = 0; i < nums.length; i++) {
List<List<Integer>> newRes = new ArrayList<>();
for (int j = 0; j <= i; j++) {
for (List<Integer> l : res) {
List<Integer> list = new ArrayList<>(l);
list.add(j, nums[i]);
newRes.add(list);
}
}
res = newRes;
}
return res;
}
}

Reference:

http://introcs.cs.princeton.edu/java/23recursion/Permutations.java.html

https://en.wikipedia.org/wiki/Heap%27s_algorithm

https://leetcode.com/discuss/62270/simple-python-solution-68ms

https://leetcode.com/discuss/55127/java-solution-easy-to-understand-backtracking

https://leetcode.com/discuss/55418/java-clean-code-two-recursive-solutions

https://leetcode.com/discuss/19510/my-ac-simple-iterative-java-python-solution

https://leetcode.com/discuss/29483/share-my-short-iterative-java-solution

https://leetcode.com/discuss/18212/my-elegant-recursive-c-solution-with-inline-explanation

http://stackoverflow.com/questions/5363619/complexity-of-recursive-string-permutation-function

https://en.m.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm

http://introcs.cs.princeton.edu/java/23recursion/JohnsonTrotter.java.html

46. Permutations的更多相关文章

  1. LeetCode - 46. Permutations

    46. Permutations Problem's Link -------------------------------------------------------------------- ...

  2. [Leetcode][Python]46: Permutations

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 46: Permutationshttps://leetcode.com/pr ...

  3. 46. Permutations 排列数

    46. Permutations 题目 Given a collection of distinct numbers, return all possible permutations. For ex ...

  4. 刷题46. Permutations

    一.题目说明 题目是46. Permutations,给一组各不相同的数,求其所有的排列组合.难度是Medium 二.我的解答 这个题目,前面遇到过类似的.回溯法(树的深度优先算法),或者根据如下求解 ...

  5. [LeetCode] 46. Permutations 全排列

    Given a collection of distinct integers, return all possible permutations. Example: Input: [1,2,3] O ...

  6. 46. Permutations 回溯算法

    https://leetcode.com/problems/permutations/ 求数列的所有排列组合.思路很清晰,将后面每一个元素依次同第一个元素交换,然后递归求接下来的(n-1)个元素的全排 ...

  7. LeetCode 【46. Permutations】

    Given a collection of distinct numbers, return all possible permutations. For example,[1,2,3] have t ...

  8. 46. Permutations——本质和树DFS遍历无异 fun: for i in nums fun(i)

    Given a collection of distinct numbers, return all possible permutations. For example, [1,2,3] have ...

  9. 46. Permutations(medium, backtrack, 重要)

    Given a collection of distinct numbers, return all possible permutations. For example, [1,2,3] have ...

随机推荐

  1. Python脚本控制的WebDriver 常用操作 <十三> 处理button group层的定位

    下面将使用webdriver来定位同一层的按钮 测试用例场景 button group就是按钮组,将一组按钮排列在一起. 处理这种对象的思路一般是先找到button group的包裹(wrapper) ...

  2. HDFS命令行操作

    启动后可通过命令行使用hadoop. (1)所有命令 (先将$HADOOP_HOME/bin加入到.bashrc的$PATH变量中) [html] view plaincopy [hadoop@nod ...

  3. Java中构造函数执行顺序的问题

    1,  先执行内部静态对象的构造函数,如果有多个按定义的先后顺序执行:而且静态类的构造函数只会被执行一次,只在其第一个对象创建时调用,即便是创建了同一个类的多个对象,例如main()函数里b1,b2创 ...

  4. 保持iOS上键盘出现时输入框不被覆盖

    如果屏幕中的内容项目比较多,键盘就可能覆盖住文本输入框之类的对象.你必须调整你的内容,使得输入框保持可见. 你会想到哪些处理方法呢? 第一种, 临时调整窗口中各个视图的大小,使得键盘从下向上占领的区域 ...

  5. Entity Framework Power Tools安装和使用

    Entity Framework Power Tools是一个由EntityFramework开发小组提供的工具,它可以从现有数据库生成Fluent款式的Code First代码. 大致来说,这个工具 ...

  6. c++类中的静态成员

    静态成员和非静态成员的区别: 类静态成员用static修饰,类的静态成员属于类本身,而不属于类的某个具体对象,静态成员被类的所有对象共享,因此某个对象对静态成员(数据成员)的修改对其对象是可见的.而类 ...

  7. C#中使用SelectionStart属性指定输入框光标位置

    今天工作中遇到一个小BUG需要修改,需求为在文本框输入的过程中,如果数字是以0开头则自动消除0 如输入012,则显示12 很容易想到在textbox的text changed事件中判断,如果text是 ...

  8. Javascript中字符串转换成Date的方法

    //字符串转成Time(dateDiff)所需方法 function stringToTime(string) { var f = string.split(' ', 2); var d = (f[0 ...

  9. Jquery-------获取网页参数

    看如下代码: function getURLParameter(name) { return decodeURI( (RegExp(name + '=' + '(.+?)(&|$)').exe ...

  10. UML类图关系大全-转

    1.关联 双向关联: C1-C2:指双方都知道对方的存在,都可以调用对方的公共属性和方法. 在GOF的设计模式书上是这样描述的:虽然在分析阶段这种关系是适用的,但我们觉得它对于描述设计模式内的类关系来 ...