全排列 全排列的一些总结

全排列的一些总结

          今天做了一道网易的笔试题——数列还原。里面用到了全排列的想法,因此学习并总结了一下全排列算法。

1、全排列问题描述

       输入一个字符串或者序列,打印出该字符串或序列中字符或元素的所有排列。例如输入字符串abc,则输出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

2、解决思路

        查了一些资料,很多都是利用递归的思想解决的。递归算法有以下几个特点:

  • 必须有可达到的终止条件,否则程序陷入死循环
  • 子问题在规模上比原问题小
  • 子问题可通过再次递归调用求解
  • 子问题的解应能组合成整个问题的解
        对于字符串全排列问题,如果能生成n-1个元素的全排列,就能生成n个元素的全排列。对于只有一个元素的集合,可以直接生成全排列。所以全排列的递归终止条件很明确:只有一个元素时。我们可以分析一下全排列的过程:
      (1)首先,我们固定第一个字符a,求后面两个字符bc的排列;
      (2)当两个字符bc排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列;
      (3)现在是把c放在第一个位置的时候了,但是记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍是和原先处在第一个位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处于第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列;
      (4)既然我们已经知道怎么求三个字符的排列,那么固定第一个字符之后求后面两个字符的排列,就是典型的递归思路了。
可以根据下面这张图来理解递归的过程:

3、编程实现
程序1:字符串实现
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<string>  
  3. #include<vector>  
  4.   
  5. using namespace std;  
  6.   
  7. void Permutation(string *s,int len)  
  8. {  
  9.     if(len == (*s).length())  
  10.         cout<<(*s)<<endl;  
  11.     else  
  12.     {  
  13.         for(int i = len;i<(*s).length();++i)  
  14.         {  
  15.             swap((*s)[len],(*s)[i]);  
  16.             Permutation(s,len+1);  
  17.             swap((*s)[len],(*s)[i]);  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. int main()  
  23. {  
  24.     string s;  
  25.     cin>>s;  
  26.     Permutation(&s,0);  
  27.   
  28.     return 0;  
  29. }  
程序2:字符数组实现
[cpp]  view plain  copy
  1. #include<iostream>    
  2. using namespace std;    
  3.   
  4. void Permutation(char* pStr, char* pBegin)    
  5. {    
  6.     if(*pBegin == '\0')    
  7.         printf("%s\n",pStr);    
  8.     else    
  9.     {    
  10.         for(char* pCh = pBegin; *pCh != '\0'; pCh++)    
  11.         {    
  12.             swap(*pBegin,*pCh);    
  13.             Permutation(pStr, pBegin+1);    
  14.             swap(*pBegin,*pCh);    
  15.         }    
  16.     }    
  17. }    
  18.     
  19. int main()    
  20. {    
  21.     char str[] = "abc";    
  22.     Permutation(str,str);    
  23.     return 0;    
  24. }   
网易编程题程序:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<vector>  
  3.    
  4. using namespace std;  
  5.    
  6. bool find(vector<int> v,int n)    //查询v中是否存在整数n  
  7. {  
  8.     for(int i = 0;i<v.size();++i)  
  9.         if(v[i]==n)  
  10.             return true;  
  11.     return false;  
  12. }  
  13.    
  14. vector<vector<int>> pv; //全局变量  
  15.    
  16. void Perm(vector<int> &v,int st)   //对v中的数字进行全排列  
  17. {  
  18.     if(st == v.size())  
  19.         pv.push_back(v);  
  20.     else  
  21.     {  
  22.         for(int i = st;i<v.size();++i)  
  23.         {  
  24.             swap(v[i],v[st]);  
  25.             Perm(v,st+1);  
  26.             swap(v[i],v[st]);  
  27.         }  
  28.     }  
  29. }  
  30.    
  31. void change(vector<int> &v,vector<int> subv,vector<int> vpos)//将v中的0用全排之后的数字分别代替  
  32. {  
  33.     for(int i = 0;i<vpos.size();++i)  
  34.         v[vpos[i]] = subv[i];  
  35. }  
  36.    
  37. int cal(vector<int> v)  //计算顺序对的个数  
  38. {  
  39.     int cnt = 0;  
  40.     for(int i = 0;i<v.size()-1;++i)  
  41.         for(int j = i+1;j<v.size();++j)  
  42.             if(v[i]<v[j])  
  43.                 ++cnt;  
  44.     return cnt;  
  45. }  
  46.    
  47. int main()  
  48. {  
  49.     int n,k,tmp;  
  50.     while(cin>>n>>k)  
  51.     {  
  52.         vector<int> v,subv,vpos;  
  53.         for(int i = 0;i<n;++i)  
  54.         {  
  55.             cin>>tmp;  
  56.             v.push_back(tmp);  
  57.         }  
  58.         for(int i = 0;i<v.size();++i)  
  59.             if(v[i]==0)  
  60.                 vpos.push_back(i);   //记录下vector<int>中0的位置  
  61.         for(int i = 1;i<=n;++i)  
  62.             if(!find(v,i))  
  63.                 subv.push_back(i);  
  64.         Perm(subv,0);  
  65.         vector<int> vcnt;  
  66.         for(int i = 0;i<pv.size();++i)  
  67.         {  
  68.             change(v,pv[i],vpos);  
  69.             vcnt.push_back(cal(v));  
  70.         }  
  71.         int vcntk = 0;  
  72.         for(int i = 0;i<vcnt.size();++i)  
  73.             if(vcnt[i]==k)  
  74.                 ++vcntk;  
  75.         cout<<vcntk<<endl;  
  76.     }  
  77.    
  78.     return 0;  
  79. }  

4、含重复元素的全排列
      上述思路解法有个缺陷:对于有重复元素的全排列,排列结果会有重复。这里直接给出全排列中去掉重复的规则:去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换。

上述第一段代码核心部分修正如下:
[cpp]  view plain  copy
  1. bool cmp(string s,int len,int i)  
  2. {  
  3.     for(int ii = len;ii<i;++ii)  
  4.         if(s[ii]==s[i])  
  5.             return true;  
  6.     return false;  
  7. }  
  8.   
  9. void Permutation(string *s,int len)  
  10. {  
  11.     if(len == (*s).length())  
  12.         cout<<(*s)<<endl;  
  13.     else  
  14.     {  
  15.         for(int i = len;i<(*s).length();++i)  
  16.         {  
  17.             if(!cmp(*s,len,i))  
  18.             {  
  19.                 swap((*s)[len],(*s)[i]);  
  20.                 Permutation(s,len+1);  
  21.                 swap((*s)[len],(*s)[i]);  
  22.             }  
  23.               
  24.         }  
  25.     }  
  26. }  

猜你喜欢

转载自blog.csdn.net/qq_42337888/article/details/80716984