博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
全排列总结
阅读量:7072 次
发布时间:2019-06-28

本文共 13503 字,大约阅读时间需要 45 分钟。

接触全排列已经好长时间了,一直没有抽空总结一下全排列的相关问题,下面来说一下!

排列

  一般地,从n个不同元素中取出mmn)个元素,按照一定的顺序排成一列,叫做从n个元素中取出m个元素的一个排列(Arrangement)。特别地,当m=n时,这个排列被称作全排列(Permutation)。

排列数公式:

                                
 
  特别,当n==m时为全排列的公式!

下一个全排列算法

  lintcode链接:

样例

  左边是原始排列,右边是对应的下一个排列。

  1,2,3 → 1,3,2

  3,2,1 → 1,2,3

  1,1,5 → 1,5,1

下一个全排列算法——first

class Solution {public:    /**     * @param nums: a vector of integers     * @return: return nothing (void), do not return anything, modify nums in-place instead     */    void nextPermutation(vector
&nums) {
//调用
中的下一个排列算法以及排序算法 if(!next_permutation(nums.begin(), nums.end())) sort(nums.begin(), nums.end(), less
()); } }

下一个全排列算法——second

class Solution {public:    /**     * @param nums: a vector of integers     * @return: return nothing (void), do not return anything, modify nums in-place instead     */    void nextPermutation(vector
&nums) { int ld = -1; if(nums.size() < 1) return ; for(int i=nums.size()-2; i>=0; --i)//找到从右边开始第一个比它右边相邻小的数的位置 if(nums[i] < nums[i+1]){ ld = i; break; } if(ld != -1){
//找到从右边开始,第一个比位置为ld所在数 大的数的位置 int rd; for(int i=nums.size()-1; i>=0; --i) if(nums[ld] < nums[i]){ rd = i; break; } swap(nums[ld], nums[rd]); //在交换之前,ld位置后面的已经是从大到小排好序的,已经没有下一个排列了 sort(nums.begin()+ld+1, nums.end());//交换之后,将ld后面的数置为最小的排列,从小到大排序 } if(ld == -1){ sort(nums.begin(), nums.end()); } }};

下一个全排列算法——third

class Solution {public:    /**     * @param nums: a vector of integers     * @return: return nothing (void), do not return anything, modify nums in-place instead     */    void nextPermutation(vector
&nums) { //挑战不使用额外的空间, 其实和方法二的思路是一样的 int n = nums.size(); for(int i=n-1; i>=0; --i) for(int j=n-1; j>i; --j) if(nums[i] < nums[j]){ swap(nums[i], nums[j]); sort(nums.begin()+i+1, nums.end()); return ; } sort(nums.begin(), nums.end()); }};

下一个全排列的思想:

 

  如上图所示,该全排列对应的下一个全排列是和绿色框里的数字以及红色框里的数字有关系的。显而易见的是红色框里的数列已经没有下一个排列了,因为已经是一个递减的序列了。为了得到下一个排列,我们将绿色框里的数字和红色框里右边起第一个比绿色框中数字大的数字交换位置(也就是4 和 5交换位置)。这样还不是下一个全排列,交换之后红色框内的序列为7 4 3 2 1 0, 将它变成递增序列 0 1 2 3 4 7,就得到了下一个全排列。

  因为,一个全排列,末尾的一段区间肯定是递减的(如上图的红色框区间),如果这段区间一直延伸到首部,那么也就没有下一个全排列了,否则找到和这段区间最近的一个数字(上图中绿色框中的数字),然后经过上述的处理就可以得到下一个全排列了。

  the second method 就是先找到绿色框数字的位置(ld), 然后在寻找红色框中右边第一个比绿色框中数字大的数字的位置(rd);

  the third method的意思就是从右边开始寻找第一对(i, j),满足nums[i]<nums[j], 对应second method中(ld, rd)。该方法没有用到额外的存储空间。

上一个全排列算法

  注:算法思想和下一个全排列的思想正好相反,步骤一致!

    lintcode链接:

样例

  给出排列[1,3,2,3],其上一个排列是[1,2,3,3]

  给出排列[1,2,3,4],其上一个排列是[4,3,2,1]

上一个全排列算法——first

class Solution {public:    /**     * @param nums: An array of integers     * @return: An array of integers that's previous permuation     */    vector
previousPermuation(vector
&nums) { //直接调用
中的算法prev_permutation() if(!prev_permutation(nums.begin(), nums.end())) sort(nums.begin(), nums.end(), greater
()); return nums; }};

上一个全排列算法——second

class Solution {public:    /**     * @param nums: An array of integers     * @return: An array of integers that's previous permuation     */    vector
previousPermuation(vector
&nums) { int ld = -1; if(nums.size() <= 1) return nums; for(int i=nums.size()-2; i>=0; --i)//找到从右边开始第一个比它右边相邻大的数的位置 if(nums[i] > nums[i+1]){ ld = i; break; } if(ld != -1){
//找到从右边开始,第一个比位置为ld所在数 小的数的位置 int rd; for(int i=nums.size()-1; i>=0; --i) if(nums[ld] > nums[i]){ rd = i; break; } swap(nums[ld], nums[rd]); //在交换之前,ld位置后面的已经是从小到大排好序的,已经没有上一个排列了 //交换之后,将ld后面的数置为最大的排列,从大到小排序 sort(nums.begin()+ld+1, nums.end(), greater
()); } if(ld == -1){ sort(nums.begin(), nums.end(), greater
()); } return nums; }};

上一个全排列算法——third

class Solution {public:    /**     * @param nums: An array of integers     * @return: An array of integers that's previous permuation     */    vector
previousPermuation(vector
&nums) { int n = nums.size(); for(int i=n-1; i>=0; --i) for(int j=n-1; j>i; --j) if(nums[i] > nums[j]){ swap(nums[i], nums[j]); sort(nums.begin()+i+1, nums.end(), greater
()); return nums; } sort(nums.begin(), nums.end(), greater
()); return nums; }};

得到所有全排列算法

  lintcode链接:

全排列算法——first

  注:非递归,由下一个全排列算法——third方法实现

class Solution {public:    /**     * @param nums: A list of integers.     * @return: A list of permutations.     */    vector
> vv; vector
> permute(vector
nums) { if(nums.size() == 0) return vv; sort(nums.begin(), nums.end()); //方法1: 非递归实现 int n = nums.size(); bool flag = true; vv.push_back(nums); while(flag){ flag = false; for(int i=n-1; i>=0; --i){ for(int j=n-1; j>i; --j) if(nums[i] < nums[j]){ swap(nums[i], nums[j]); sort(nums.begin()+i+1, nums.end()); vv.push_back(nums); flag = true; break; } if(flag) break; } } return vv; }};

全排列算法——second

class Solution {public:    /**     * @param nums: A list of integers.     * @return: A list of permutations.     */    vector
> vv; vector
> permute(vector
nums) { if(nums.size() == 0) return vv; sort(nums.begin(), nums.end()); //方法2:调用
中的next_permutation() do{ vv.push_back(nums); }while(next_permutation(nums.begin(), nums.end())); return vv; }};

全排列算法——third

  注:递归思路:一共有nn个位置,然后每个位置枚举可能出现的数字(注意处理重复数字的情况)

class Solution {public:    /**     * @param nums: A list of integers.     * @return: A list of permutations.     */    vector
> vv; /// int nn;//没有 unique 之前的数组的大小 map
mp; void dfs_1(int cur, vector
&v, vector
nums){ if(cur >= nn){ vv.push_back(v); return; } for(int i=0; i
> permute(vector
nums) { if(nums.size() == 0) return vv; sort(nums.begin(), nums.end()); //方法3:递归 for(int i=0; i
v; dfs_1(0, v, nums); return vv; }};

全排列算法——forth

  注:递归思路:每一个数,不断的和后面的数交换位置,每交换一次就会得到一个新的排列

class Solution {public:    /**     * @param nums: A list of integers.     * @return: A list of permutations.     */    vector
> vv; void dfs_2(int ld, int rd, vector
nums){ if(ld == rd){ vv.push_back(nums); return ; } for(int i=ld; i<=rd; ++i){ swap(nums[ld], nums[i]); dfs_2(ld+1, rd, nums); swap(nums[ld], nums[i]); } } vector
> permute(vector
nums) { if(nums.size() == 0) return vv; sort(nums.begin(), nums.end()); dfs_2(0, nums.size()-1, nums); return vv; }};

排列序号(按字典序排序属于第几个全排列)

  注:不含重复数字的排列!

  lintcode链接:

样例

  例如,排列[1,4,2]是第2个全排列。

排列序号算法

  算法思想请参考:

class Solution {public:    /**     * @param A an integer array     * @return a long integer     */    long long permutationIndex(vector
& A) {
//一个一个来肯定会超时// vector
permu(A.begin(), A.end()); // sort(permu.begin(), permu.end()); // int cnt = 0; // do{ // int i; // for(i=0; i
=A.size()) break; // }while(next_permutation(permu.begin(), permu.end())); // return cnt; vector
a; int len = A.size(); int cnt[len]; cnt[len-1] = 0; a.push_back(A[len-1]); for(int i=len-2; i>=0; --i){
//统计每个数后面有多少个比它小的数的个数 vector
::iterator it = lower_bound(a.begin(), a.end(), A[i]); cnt[i] = it-a.begin(); a.insert(it, A[i]); } long long ans=1, fac=1, c=1; for(int i=len-2; i>=0; --i) ans += (fac*=c++)*cnt[i]; return ans; }};

第k个排列

  注:给定 n 和 k,求123..n组成的排列中的第 k 个排列。

  lintcode链接:

样例

  对于 n = 3, 所有的排列是:123, 132, 213, 231, 312, 321.

  如果 k = 4, 第4个排列为,231.

康托展开的公式 :

  X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!,ai为整数,并且0<=ai<i(1<=i<=n)

  适用范围:没有重复元素的全排列

例题:

  找出第16个n = 5的序列(12345)。康托展开只要O(n)就行 ,下面来说说具体怎么做:

  根据第一行的那个全排列公式,15 / 4! = 0 …15  =>  有0个数比它小的数是1,所以第一位是1

  拿走刚才的余数15,用15 / 3! = 2 …3   =>  剩下的数里有两个数比它小的是4(1已经没了),所以第二位是4

  拿走余数3, 用 3 / 2! = 1 …1   =>  剩下的数里有一个数比它小的是3,所以第三位是3

  拿走余数1, 用 1/  1! = 1 …0    =>  剩下的数里有一个数比它小的是 5(只剩2和5了),所以第四位是5

  所以排列是 1,4,3,5,2

第k个排列算法

 

class Solution {public:    /**      * @param n: n      * @param k: the kth permutation      * @return: return the k-th permutation      */         string getPermutation(int n, int k) {        if(n==1) return "1";        int f[n+1];        bool use[n+1];        f[1] = 0;        f[2] = 1;        memset(use, false, sizeof(use));        for(int i=3; i<=n; ++i)            f[i] = f[i-1]*(i-1);        string ans = "";        --k;//要计算的排列之前有多少个排列        for(int i=n; i>=2; --i){            int cnt = 0;            int c = k/f[i];//假设该排列的这位数是x,c就是比x小的数(之前没有用过)的个数            k%=f[i];            for(int j=1; j<=n; ++j){
//寻找符合要求的x的值 if(!use[j]){ if(cnt == c){ ans += j+'0'; use[j] = true; break; } ++cnt; } } } for(int j=1; j<=n; ++j) if(!use[j]){ ans += j+'0'; break; } return ans; }};

 

带重复元素的排列

  题目链接:

带重复元素的排列——first

  思路:next_permutation()本身支持带重复元素的全排列

class Solution {public:    /**     * @param nums: A list of integers.     * @return: A list of unique permutations.     */    vector
> ans; vector
> permuteUnique(vector
&nums) { // write your code here sort(nums.begin(), nums.end()); do{ ans.push_back(nums); }while(next_permutation(nums.begin(), nums.end())); return ans; }};

 

带重复元素的排列——second

  思路:枚举每个位置肯能出现的数字。

class Solution {public:    /**     * @param nums: A list of integers.     * @return: A list of unique permutations.     */    vector
> ans; map
cnt;//记录每个数字在nums中出现的次数 void dfs(int n, vector
&nums, vector
&v){ if(v.size() >= n){ ans.push_back(v); return ; } for(int i=0; i
> permuteUnique(vector
&nums) { // write your code here sort(nums.begin(), nums.end()); for(int i=0; i
v; dfs(n, nums, v); return ans; }};

 

带重复元素的排列——third

  思路:和 “全排列算法——first”方法类似,唯一不同的是下面代码中红色部分

class Solution {public:    /**     * @param nums: A list of integers.     * @return: A list of unique permutations.     */    vector
> ans; vector
> permuteUnique(vector
&nums) { // write your code here sort(nums.begin(), nums.end()); bool flag = true; while(flag){ flag = false; ans.push_back(nums); for(int i=nums.size()-1; i>=0; --i){ for(int j=nums.size()-1; j>i; --j) if(nums[i] < nums[j]){ flag = true; while(j-1>i && nums[j-1]==nums[j]) --j;//如果前面有相同的数字,找到最左边的数字 swap(nums[i], nums[j]); sort(nums.begin()+i+1, nums.end()); break; } if(flag) break; } } return ans; }};

 

转载地址:http://qjkml.baihongyu.com/

你可能感兴趣的文章
Js判断是否是直接进入本页面的
查看>>
GOlang eclipse install
查看>>
Centos7 hostname重启失效
查看>>
Postgre SQL连接服务器失败
查看>>
小程序之短信验证
查看>>
App Store优化推广 如何提高海外搜索排名
查看>>
POJ3580 SuperMemo(Splay的区间操作)
查看>>
Android Service解析
查看>>
CentOS7-部署kubernetes
查看>>
hdu 4001 To Miss Our Children Time( sort + DP )
查看>>
日常note
查看>>
Leetcode 727. Minimum Window Subsequence
查看>>
java切换jdk版本
查看>>
hdu 1005 Number Sequence zoj 1105
查看>>
VLAN
查看>>
Oracle12c 性能优化攻略:攻略1-2:创建具有最优性能的表空间
查看>>
yum install 报错[Errno 14] curl#37 - Couldn't open file /mnt/repodata/repomd.xml
查看>>
box-sizeing
查看>>
bzoj 3669 [Noi2014]魔法森林
查看>>
CentOS为中文显示
查看>>