排列组合
Index
排列
下一个排列
LeetCode - 31. 下一个排列
题目描述
思路
相邻的两个排列有最长公共前缀,然后找到需要交换的高位和低位
根据字典序的定义,依照如下步骤寻找下一个排列
从后往前找需要改变的高位 hi,即第一个降序元素的位置
从后往前找需要交换的低位 lo,即第一个大于 nums[hi] 的位置
交换 nums[lo] 与 nums[hi]
反转 hi 之后的序列,即 nums[hi+1: n)
C++
上一个排列
LintCode - 51. 上一个排列
问题描述
思路
实际上就是下一个排列的逆过程
从右往左找第一个升序的位置 hi
从右往左找第一个小于 nums[hi] 的位置 lo
交换 nums[lo] 和 nums[hi]
反转 hi 之后的位置
C++
STL 提供的实现(下一个排列、上一个排列) TODO
STL 提供了两个函数用于生成排列
这两个函数均以字典序比较函数
lexicographical_compare()为基础生成下一个或上一个排列因此在使用这两个函数前,需要先对原序列进行排序
C++
第 k 个排列
LeetCode - 60. 第k个排列
问题描述
思路
因为字典序的性质,实际不需要求出前 k-1 个序列
整体思路有点像桶排序
以
{1 2 3 4 5}为例,找出其中第14个序列
C++
全排列(无重复)
LeetCode 46. 全排列
题目描述
思路 1
利用下一个排列,先对数组排序,然后不断生成下一个排列
思路 2
深度优先搜索
易知,当序列中的元素不重复时,存在
n!种不同的排列;考虑第一个位置,有 n 种可能
当选定了第一个位置,第二个位置有 n-1 种可能
因为每次搜索的状态数是递减的,所以这里的 dfs 是一个循环递归的过程
基于插入的写法
代码量多一点,但比较好理解
```C++ class Solution { vector > ret; vector tmp; vector used; int n = 0;
void dfs(vector& nums, int step) { if (tmp.size() == n) { ret.push_back(tmp); return; }
}
public: vector > permute(vector& nums) { n = nums.size(); used.resize(n, 0);
};
全排列(有重复)
LeetCode - 47. 全排列 II
题目描述
思路 1
使用无重复时的方法,用 set 剔除重复(不推荐)
思路 2
先对原序列排序,使相同的元素相邻;此时只处理第一个相同元素,其余跳过;
基于插入的写法
基于交换的写法
【注】全排序的时间复杂度
不重复情况下,n 个元素的不同全排列为
n!个,所以算法的时间复杂度至少为O(N!)因此,全排列算法对大型的数据是无法处理的
组合
组合(n 选 k,无重复)
LeetCode - 77. 组合
问题描述
思路
带回溯的深度优先搜索,类似全排列(无重复)
C++
组合(n 选 k,有重复)
(未验证)
如果要求每个组合中不重复,则可以先去重,再按照无重复的做法
如果不要求去重,则直接按照无重复的做法即可
组合总和(数字不重复但可重复使用)
LeetCode - 39. 组合总和
思路
深度优先搜索
关键在于每个数字可以重复使用
C++
组合总和 2(存在重复数字但每个数字只能使用一次)
LeetCode - 40. 组合总和 II
思路
DFS,关键是如何去除重复情况
C++
代码说明 1
以
为例这段代码实际上并不会过滤
——i == step的情况真正重复的情况是
和
,而这段代码的目的是过滤
——i > step的情况
组合总和 3(数字不重复且指定数量)
LeetCode - 216. 组合总和 III
问题描述
思路
在组合总和(数字不重复但可重复使用)上稍作修改即可
C++
【说明】
字典序
在处理排列问题时,通常时根据字典序来生成下一个排列
在字典序中,记序列的升序为第一个排列,降序为最后一个排列
高位与低位
对序列中任意两个位置而言,靠近左侧的为高位,靠近右侧的为低位
生成排列的过程就是不断增大高位,减小低位的过程
关于 for(i=0;..) 与 for(i=step;..) 的说明
for(i=0;..) 与 for(i=step;..) 的说明for(i=0;..)需配合used标记使用for(i=step;..)所有组合问题
简单来说,以
{1 2 3 4}为例for(i=0;..)用于以下情况used 用于标记开头的
1、2等
for(i=step;..)用于以下情况一般不需要
used标记
【注】关于 dfs(step+1)、dfs(i+1)、dfs(i) 的说明
dfs(step+1)、dfs(i+1)、dfs(i) 的说明(以下均为个人小结,并没有严格验证)
dfs(step+1) 和 dfs(i+1)
dfs(step+1) 和 dfs(i+1)以不重复集合
{1 2 3 4}为例说明:排列问题中用过的元素还可能被再次使用;
而组合问题中使用过的元素,之后不再使用
正是由于这个区别导致排列中应该使用
dfs(step+1),而组合中应该使用dfs(i+1)
dfs(i+1) 和 dfs(i)
dfs(i+1) 和 dfs(i)在组合总和问题中,还用到了
dfs(i)一方面,它跟组合问题类似,用过的数字不再使用;因此使用的是
i而不是step另一方面,每个数字可以重复使用,因此使用的是
dfs(i)而不是dfs(i+1)
最后更新于
这有帮助吗?