递归---排列问题
来源:互联网 发布:云计算细分市场格局 编辑:程序博客网 时间:2024/06/06 10:09
一、排列问题
设R={r1,r2,...,rn}是要进行排列的n个元素,Ri=R-{ri}。集合x中元素的全排列记为Perm(X)。
(ri)Perm(X)表示在全排列Perm(X)的每一个排列前加上前缀ri得到的排列。
R的全排列可归纳如下:
当n=1时,Perm(R)=(r),其中r是集合中唯一的元素;
当n>1时,Perm(R)由(r1)Perm(R1),(r2)Perm(R2),(r3)Perm(R3)......(rn)Perm(Rn)构成。
实现思想:将数组中所有的数都与第一个数交换,这样就总是求后n-1个数的全排列(其实,就是把数组中每一个数都分别放到第一个位置,然后求剩下n-1个数的全排列)
【示例】
当n=3,并且E={a,b,c},则:
perm(E)=a.perm({b,c}) + b.perm({a,c}) + c.perm({a,b})
perm({b,c})=b.perm(c) + c.perm(b)
a.perm({b,c})=ab.perm(c) + ac.perm(b)=ab.c + ac.b=(abc, acb)
依递归定义,设计Perm(R)的递归算法如下:
#include <iostream>using namespace std;template <class Type>inline void Swap(Type &a,Type &b);template <class Type>void Perm(Type list[],int k,int m);int main(){ int list[3]; for(int i=0; i<3;i++) { list[i] = i+1; } Perm(list,0,2); return 0;}template <class Type>inline void Swap(Type &a,Type &b){ Type temp = a; a = b; b = temp;}template <class Type>void Perm(Type list[],int k,int m){ //只剩下一个元素 if(k == m){ for(int i=0; i<=m; i++) { cout<<list[i]<<" "; } cout<<endl; } else { //将list[k:m}中的每一个元素分别与list[k]中的元素交换 //然后递归计算list[k+1:m]的全排列,将计算结果作为list[0:k]后缀 for(int i=k; i<=m;i++){ Swap(list[k],list[i]); Perm(list,k+1,m); Swap(list[k],list[i]); } }}
class Solution { vector<vector<int>> result;public: void Perm(vector<int>& nums,int k,int n){ if(k>=n) return; if(k==n-1){ /*for(int i=0;i<n;++i) cout<<nums[i]; cout<<endl;*/ result.push_back(nums); } else{ for(int i=k;i<n;++i){ swap(nums[k],nums[i]); Perm(nums,k+1,n); swap(nums[k],nums[i]); } } } vector<vector<int>> permute(vector<int>& nums) {//nums中数字不同,没有重复的 int n=nums.size(); if(n<=0) return result; Perm(nums,0,n); return result; }};
当数组中有重复的元素时:leetcode题目2
class Solution { vector<vector<int>> result;public: void Perm1(vector<int> nums,int i,int j){ if(i==j-1){ result.push_back(nums); return; } else{ for(int k=i;k<j;k++){ if(k!=i&&nums[k]==nums[i]) continue; swap(nums[k],nums[i]); Perm1(nums,i+1,j); } } } vector<vector<int>> permuteUnique(vector<int>& nums) { sort(nums.begin(),nums.end()); Perm1(nums,0,nums.size()); return result; }};leetcode题目:下一个全排列
class Solution {public: void nextPermutation(vector<int>& nums) { int n=nums.size(); if(n<=1) return; int k=-1; for(int i=n-2;i>=0;i--){ if(nums[i]<nums[i+1]){//从后往前,找到第一个非递增的数的下标k,例:1,2,5,4,3,3,则找到2,下标为1 k=i; break; } } if(k==-1){//整个数组为非递增 reverse(nums.begin(),nums.end()); return; } int l=-1; for(int j=n-1;j>k;j--){//从后往前,找到第一个大于之前找到的那个数nums[k] if(nums[j]>nums[k]){ l=j; break; } } swap(nums[k],nums[l]); reverse(nums.begin()+k+1,nums.end()); }};
以上这个算法思想:
1.从右往左寻找连续递增序列,例:1,3,5,4,2,其中5,4,2为递增序列
2.从上述序列中找一个比它前面的数(3)大的最小的数(4),并且交换这两个数,1,3,5,4,2--->1,4,5,3,2,交换后的依然是递增序列
3.新的递增序列逆序,1,4,5,3,2--->1,4,2,3,5
leetcode题目:全排列中第k个排列
康托展开的公式:(不用记,看形势就行,下面会有例子)
X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
ai为整数,并且0<=ai<i(1<=i<=n)
适用范围:没有重复元素的全排列
来自点击打开链接
第一类题:N个数的第k个排序,例子,1,2,3,4共有4!种排列,1234,1243,1324等等。按顺序应该是
1234
1243
1324
1342
1423
1432等等
可以通过STL中next_permutation(begin, end);来算下一个全排列,理论上你要算n个数的第k个排列只要调用k-1次next_permutation()就行,但是一般来说肯定会超时的,因为next_permutation的时间复杂度是O(n)(如果自己写出来next_permutation时间复杂度比n大就要注意了,其中一个容易疏忽的地方是最后排序可以用reverse而不是sort)。所以如果用这个的话时间复杂度是O(N^2)。
而用康托展开只要O(n)就行,下面来说说具体怎么做:
题目:找出第16个n = 5的序列(12345)
首先第十六个也就是要前面有15个数,要调用15次next_permutation函数。
根据第一行的那个全排列公式,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
第二类题:已知是n = 5,求14352是它的第几个序列?(同一道题)
用刚才的那道题的反向思维:
第一位是1,有0个数小于1,即0* 4!
第二位是4,有2个数小于4,即2* 3!
第三位是3,有1个数小于3,即1* 2!
第四位是5,有1个数小于5,即1* 1!
第五位是2,不过不用算,因为肯定是0
所以14352是 n = 5的第 0 + 12 + 2 + 1 + 0 = 15 + 1(求的是第几个,所以要加一) = 16
第16个,跟刚才那道题一样,证明对了
class Solution {public: string getPermutation(int n, int k) {//康托展开的公式 string res; string nums = "123456789";//因为已知n:1~9 int f[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};//0!,1!,2!,......,9! --k;//需要k-1次 for (int i = n; i >= 1; --i) { int j = k / f[i - 1]; k %= f[i - 1]; res.push_back(nums[j]); nums.erase(nums.begin() + j); } return res; }};ps:
把一个问题映射到二进制数,有很多好玩的题目:
有1000个一模一样的瓶子,其中有999瓶是普通的水,有一瓶是毒药。任何喝下毒药的生物都会在一星期之后死亡。现在,你只有10只小白鼠和一星期的时间,如何检验出哪个瓶子里有毒药?
1. 将所有瓶子编号,1、2、3、... 、1000;
2. 将所有编号转换成对应二进制数, 0000000001,0000000010,0000000011,...,1111101000;
3. 给1号小白鼠吃所有二进制数最低位为1的药,如,1、3、5、7、...
给2号小白鼠吃所有二进制数中,次低位为1的药,如,2、3、4、6、...
......
给10号小白鼠,吃所有二进制数中,右数第10位为1的瓶子对应的药,如,512、513、514、...
4. 最后,根据死去的小白鼠就可以推断出是哪瓶为毒药,如,第2、4、7、9个小白鼠死了,那么对应的二进制数为0101001010,即,第660瓶 为毒药
(有1000瓶老鼠药和10只老鼠,其中一瓶有毒,老鼠喝了两天会挂,如何在两天之内找到哪瓶有毒:老鼠排成一列,每只老鼠看成一位bit,1~1000转化为二进制,每瓶都给位为1的对应的老鼠喝,最后挂掉的老鼠的位置1,其余置0,对应的号就是那瓶有毒的药瓶的号)
- 递归求排列问题
- 递归 全排列 问题
- 递归-排列问题
- 排列问题(递归)
- 递归之排列问题
- 递归---排列问题
- 递归-排列问题
- 排列问题(递归算法)
- 递归求全排列问题学习
- 全排列问题 递归实现
- 递归求解全排列问题
- 递归解决全排列问题
- 递归解决全排列问题
- 全排列问题递归实现
- 全排列问题 递归算法
- 递归实现全排列问题
- 递归算法之排列问题
- 递归实现全排列问题
- vue-ref-$refs用法
- 常用加密算法的Java实现总结(二)
- siebel escript入门——函数
- CSS 设置z-index无效原因
- 【如何快速的开发一个完整的iOS直播app】(采集篇)
- 递归---排列问题
- git使用详解
- java窗体简易控件的实现
- springcloud实战之8 断路器-仪表盘-单例监控(Hystrix)
- “科林明伦杯”哈尔滨理工大学第七届程序设计团队赛 A. An Easy Geometry Problem(计算几何)
- 1060 最复杂的数
- 分布式框架Dubbo入门
- 让你的chrome控制台完全支持jQuery语法
- 从Github中的Commit历史移除敏感文件