非負整數可重集去重&排序+獲得可重集的全排列的幾種方法


 

非負整數可重集O(n)去重並排序

可重集是指元素可重復的集合,對於在一定區間內的正整數集,比如[1,n],我們可以在不不使用任何額外空間(包括不使用O(1)的空間)的情況下,用O(n)的時間復雜度完成集合的去重並排序,這種O(n)的算法,是理想的聯機算法。

思路:本質上和桶排序類似,用數組下標來表示存在的元素,數組中的元素作為flag。

對於正整數可重集來說,打標記的方法可以是將元素變負(思考,為什么不是隨便賦一個規定的值),負整數依次類推。

對於元素屬於[1,n]的集合(n為元素個數),我們可以用下面的代碼完成上述操作並取出元素,總時間是O(2n)

 1 void removeDuplicates(int *a,int len){
 2     for(int i=0;i<len;i++)
 3         a[abs(a[i])-1] = -abs(a[abs(a[i])-1]);//如果元素a[i]存在,則將a[abs(a[i])-1] 變負,下標0 ~ n-1 對應1 ~ n 
 4     for(int i=0;i<len;i++)if(a[i]<0)//如果a[i]<0 ,則說明i+1存在 ,取出 
 5         printf("%d ",i+1);
 6 }
 7 int main(){
 8     int seq[] = {5,3,4,2,3,1,2,2,5};
 9     removeDuplicates(seq,sizeof(seq)/sizeof(int));
10      return 0;
11 }

 

 

獲得可重集全排列:

自己玩:

可重集是指元素可重復的集合,可重集的全排列通常可以遞歸地進行求解。

對於n個元素不重復的集合來說,我們可以遞歸為:

  1. 將第k個元素(k=1,2...n)放到集合首部
  2. 求解剩下n-k個元素的集合的全排列
  3. 重復1和2,直到集合的元素為空時,打印整個集合

實現的代碼(此處是以字符串為例),其中len表示字符串s的長度。注意,這里s必須定義為數組,如果定義為指針,將會引發錯誤,具體請看我的另一篇博客:C++指針和數組的區別中的情況2

 1 void swap(char &i,char &j){
 2     char t=i;i=j;j=t;
 3 }
 4 void permutation(char s[],int left,int len){
 5     if(left==len)printf("%s\n",s);
 6     else{
 7         for(int k=left;k<len;k++){
 8             if(s[left]!=s[k])swap(s[left],s[k]);
 9             //遞歸求解n-k個元素的全排列
10             permutation(s,left+1,len);
11             if(s[left]!=s[k])swap(s[left],s[k]);
12         }
13     }
14 }

需要注意:這種實現不是遵從字典序的實現

當我們需要打印可重集的全排列時,我們只需對遞歸調用的部分稍作改動

  1. 重復的情況要保證出現,所以,當left==k的時候,代表第一次遞歸,此時,應當保留
  2. 除了1之外,如果s[left]和s[k]仍有相等情況,則不應交換和遞歸,因為此時若遞歸,會造成重復

簡單修改上述代碼,實現如下:

 1 void swap(char &i,char &j){
 2     char t=i;i=j;j=t;
 3 }
 4 void permutation(char s[],int left,int len){
 5     if(left==len)printf("%s\n",s);
 6     else{
 7         for(int k=left;k<len;k++){
 8             //增加了上文的兩個判定條件 
 9             if(k==left||s[left]!=s[k]){
10                 swap(s[left],s[k]);
11                 permutation(s,left+1,len);
12                 swap(s[left],s[k]);
13             }
14         }
15     }
16 }

同樣,這種實現不是遵從字典序的實現

 

當然,我們很多時候都需要按照字典序進行排列,說實話,我覺得我是很討厭手寫這個的,畢竟相當的麻煩,所以,就有了下面這個:

黑科技:STL中的next_permutation(s,s+n)

1 #include<algorithm>
2 using namespace std;
3 void permutation(char s[],int len){
4     sort(s,s+len);//一定要先排序
5     do{
6         puts(s);
7     }while(next_permutation(s,s+len));
8 }

這是貨真價實的字典序的全排列,今天就到這,拜拜~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM