题目:求所有全排列

难度:Medium

题目内容

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

我的思路:每种情况中,每一个元素只出现一次,只是之间的顺序不同,那么马上就联想到和之前的那个利用队列求按键输出所有可能的字母组合题一样:LeetCode第[17]题(Java):Letter Combinations of a Phone Number,只要把其中思路稍作修改即可。

步骤:

1、首先创建一个队列ans,往里面放一个空的list;

2、对ans队列进行while循环,只要队头List的长度小于输入数组的长度,那么说明还没有排完则进入循环;

3、取出队头List,并且对输入数组进行循环,如果循环内发现当前List里没有某个元素,则复制队头List,然后把此元素传入,再把复制品放入ans队尾。

为什么要复制一个新的List?

  因为,List是一个引用类型,取出队头后的循环判断的都是同一个队头List,而不是新的。

以【1,2,3】为输入数组,图示步骤如下:

最初ansList:

【【】】

第一大轮while循环后(此一大轮包括若干轮):

【【1】【2】【3】】

第二大轮循环后:

【【1,2】,【1,3】,

【2,1】,【2,3】,

【3,1】,【3,2】】

第三大轮循环后:

【【1,2,3】,【1,3,2】,

【2,1,3】,【2,3,1】,

【3,1,2】,【3,2,1】】

完毕

我的代码:

     public List<List<Integer>> permute(int[] nums) {
LinkedList<List<Integer>> ans = new LinkedList<List<Integer>>();
ans.add(new ArrayList<Integer>());
while (ans.peek().size() < nums.length) {
List<Integer> temp = ans.poll();
for (int i = 0; i < nums.length; i++) {
if (!temp.contains(nums[i])) {
List<Integer> inner = new ArrayList<Integer>(temp); // 复制一个新的
inner.add(nums[i]);
ans.add(inner);
}
}
}
return ans;
}

我的复杂度:O(N3)  ———因为contains()方法其实也循环了一次。

编码过程中的问题

1、忘了要复制出一个新的;

2、忘了怎么复制新List: new ArrayList<Integer>(temp);

答案代码

 public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
backtrack(list, new ArrayList<>(), nums);
return list;
} private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums){
if(tempList.size() == nums.length){
list.add(new ArrayList<>(tempList));
} else{
for(int i = 0; i < nums.length; i++){
if(tempList.contains(nums[i])) continue; // element already exists, skip
tempList.add(nums[i]);
backtrack(list, tempList, nums);
tempList.remove(tempList.size() - 1);
}
}
}

答案复杂度:O(N3

答案思路

利用了递归的思想,新建一个递归方法,传入 三个参数: 答案List 、元素List、输入数组

递归方法的内部逻辑:

1、如果当前元素List的长度已经与输入数组一样,那么说明此元素List已经排列完毕,所以直接加入答案List

2、否则,对输入数组进行循环,如果当前元素List没有包含某元素,则将此元素传入当前元素List,并调用递归方法传入( 答案List 、元素List、输入数组),再将当前元素List刚加入的那个元素给删了。

主要逻辑和我的方法是一个思想,就是对对输入数组进行循环,不包括则加入,并再回到最初判断。

不过有个有意思的地方就是,他没有用复制一个新的List来解决复用的问题,而是把新加入的那个元素(最后一个)给remove掉,和复制一个新的List是同样的效果,不过这种方法降低了空间复杂度

但是请【注意】,这个删除的方法并不适用我的那个方法

  因为,假如我在tempList加入新元素后直接add进入 ansList,然后再删去temp刚加入的新元素,代码如下:

                if (!temp.contains(nums[i])) {
temp.add(nums[i]);
ans.add(temp);
temp.remove(temp.size()-1);
}

那么由于temp是个List,所以加入进ans的也是一个引用,也就是说ans里的和我要做remove操作的List是同一个List,所以这段代码执行完后ans只会加入一个长度没有变化的tempList,所以ans的队头永远都是空的List,while循环判断就会进入死循环。

那为什么递归方法却可以用删除

  因为递归方法在remove()方法的前面,当执行到remove()的时候,关于新的tempList的操作已经完成了,所以此时remove然后变回原来的tempList不会产生任何影响。

【并且,“复制一个新的List”,在递归方法也是可以用的,请自行验证哈~】

扩展:47. Permutations II

给定一组可能包含重复(46题元素都不同)的数字,返回所有可能的惟一排列。(其实本题还和LeetCode第[78]题Subsets(求子集)扩展——第[90]题:Subsets 2 这个很像)

Example:

Input: [1,1,2]
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

 答案代码

 public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
return list;
} private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){
if(tempList.size() == nums.length){
list.add(new ArrayList<>(tempList));
} else{
for(int i = 0; i < nums.length; i++){
if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue;
used[i] = true;
tempList.add(nums[i]);
backtrack(list, tempList, nums, used);
used[i] = false;
tempList.remove(tempList.size() - 1);
}
}
}

答案思路

相对于本题多出了以下几点不同

1、因为包含相同的元素,所以需要排序以方便判断左右相同元素;

2、因为每个tempList都是一个新的排列,然而在此排列中又必须要有标志位来标志本次排列中已经使用过的元素;

3、如果(当前元素是被使用过的    或者   (当前元素和前一个元素相等  并且  前一个元素是没被使用过的))

    此时说明此元素值是被使用过的,所以continue。

在此特地解释一下为什么要是“ 前一个元素是没被使用过的” ,没有打错就是没有使用过的。

  因为,每一次排列都是按顺序进行循环的,直到关于此元素的后续全部循环结束后才会回溯到此将此元素的used再设回为没使用,所以,如果循环到后一个元素,而前面的元素已经是没使用过的,则说明前面的组合已经全部放进答案List中去了。

举例:【-1,-1,-1,2】

以当首先对第一个元素开头的排列进行循环,假设已经以第一个元素和第二个元素开头的都已经全部排列,已经开始回溯到第一个元素和第三个元素开头了,

此时的used为【true,false,false,false】

而因为此时第三个元素和前一个元素(第二个元素)是相等的,而前一个已经为false,说明前一个已经是进行循环完毕而回溯过的,所以此时,第一个元素和第三个元素开头则为第一个元素和第二个元素开头的重复排列,所以跳过。

LeetCode第[46]题(Java):Permutations(求所有全排列) 含扩展——第[47]题Permutations 2的更多相关文章

  1. LeetCode第[18]题(Java):4Sum 标签:Array

    题目难度:Medium 题目: Given an array S of n integers, are there elements a, b, c, and d in S such that a + ...

  2. LeetCode第[1]题(Java):Two Sum 标签:Array

    题目: Given an array of integers, return indices of the two numbers such that they add up to a specifi ...

  3. LeetCode第[1]题(Java):Two Sum (俩数和为目标数的下标)——EASY

    题目: Given an array of integers, return indices of the two numbers such that they add up to a specifi ...

  4. [Leetcode][Python]46: Permutations

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

  5. LeetCode 【46. Permutations】

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

  6. 【一天一道LeetCode】#46. Permutations

    一天一道LeetCode系列 (一)题目 Given a collection of distinct numbers, return all possible permutations. For e ...

  7. LeetCode第[79]题(Java):Word Search(矩阵单词搜索)

    题目:矩阵单词搜索 难度:Medium 题目内容: Given a 2D board and a word, find if the word exists in the grid. The word ...

  8. LeetCode第[21][23]题(Java):Merge Sorted Lists

    题目:合并两个已排序链表 难度:Easy 题目内容: Merge two sorted linked lists and return it as a new list. The new list s ...

  9. LeetCode(46)Permutations

    题目 Given a collection of numbers, return all possible permutations. For example, [1,2,3] have the fo ...

随机推荐

  1. etcd跨机房部署方案

    使用ETCD做为元数据方便快捷,但是谈到跨机房灾备可能就迷糊了,我们在做节日灾备的时候同样遇到了问题, 通过查阅官方文档找到了解决方案,官方提供make-mirror方法,提供数据镜像服务 注意: m ...

  2. cygwin简介,安装及卸载(体验UNIX & Linux环境)

    对于爱好者或初学者来说,为了体验UNIX & Linux环境,去安装虚拟机或双系统稍显麻烦,cygwin是一个很好的选择 具/原料   安装windows的电脑一台(可以联网) 法/步骤   ...

  3. VC的CListCtrl控件

    1. CListCtrl 样式及设置 2. 扩展样式设置 3. 数据插入 4. 一直选中Item 5. 选中和取消选中Item 6. 得到CListCtrl中所有行的checkbox的状态 7. 得到 ...

  4. 外观模式(Facade) Adapter及Proxy 设计模式之间的关系 flume

    小结: 1. 外观模式/门面模式 Facade  往是多个类或其它程序单元,通过重新组合各类及程序单元,对外提供统一的接口/界面. Proxy(代理)注重在为Client-Subject提供一个访问的 ...

  5. img标签使用onload进行src更改时出现的内存溢出问题

    最近在开发时需要在img标签加载完成后修改src属性,使用了onload方法. 但是在方法体中最后没有把onload事件指向null, 导致了循环调用onload方法,CPU占用一直居高不下,最后只要 ...

  6. python基础之类的内置__setattr__,__delattr__,__getattr__和 二次加工标准类型(包装)

    一.内置attr:__setattr__,__delattr__,__getattr__ __setattr__ #添加/修改属性会触发它的执行 __delattr__ #删除属性的时候会触发 __g ...

  7. pycharm的MySQLdb模块导不进去时解决办法

    一.Windows下python2.7安装MySQLdb模块 根据Python多少位下载对应版本: 32位:https://pypi.python.org/pypi/MySQL-python/1.2. ...

  8. mysql 客户端命令行下 直接查询并导出数据

    mysql原来还能这么导出数据,涨知识了. 方式1: select ....(sql语句) INTO OUTFILE   '/var/lib/mysql/msg_data.csv ' (导出的文件位置 ...

  9. USB 3.0:那些你需要知道的事

    在过去14年来,通用串行总线(USB)已成为计算机和外部设备之间的标准接口.不管是移动硬盘.相机.鼠标.键盘.打印机,还是扫描仪,它们和计算机之间的数据传输一般均采用USB线.USB接口也的确是“通用 ...

  10. Linux运维工程师:30道面试题整理

    1.linux 如何挂在 windows 下的共享目录 mount.cifs //192.168.1.3/server /mnt/server -o user=administrator,pass=1 ...