回溯法经典算法 求集合中所有的子集
今天我們來看一下子集的問題。
題目描述:給定一個任意集合A,集合的長度為Length,讓你打印出這個集合中所包含的所有子集。
題目分析:此問題實際上也是一個遍歷樹的問題,進行遍歷每一個子元素,再進入下層函數時候記錄上層結果,加入到下層函數中,再存儲起來。其實總結器來他就是一顆完全二叉樹。以下我們結合圖來具體的說一下:
我們以集合{1,2,3}來對此畫樹狀圖理解一下。圖如下:
以此我們可以得到集合{1,2,3}中的子集有{},{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3};
以此我們就可以用遞歸算法對其進行代碼操作。
具體的遞歸方法:對于一個含有n個元素的集合,我們要對其進行找子集,首先我們需要先定義一個數組,以此來存儲對這N位的2進制數來表示這個集合,第i為如果為1則表示第i個元素在表示的這個集合中,否則則不再這個集合中。簡單的來說就是首先我們先讓這個數組的第一個元素為No,然后他會一直往下找,如果到遞歸結束時還是No,就將它打印出來,然后進行回溯到上一層。然后我們就把這個元素定位Yes,然后在進行在Yes條件下進行遞歸尋找,我們每次進行到遞歸結束的條件時,我們對其進行回溯。可能我說的還是有點理解不了,大家可以結合著上面的圖來看代碼:
#include<stdio.h> #include<string.h> #include<assert.h>int arr[]={0};//存儲當前第i個位置元素包含還是不包含 void Ziji(char brr[],int k,int length)//k是遞歸的層次,k與集合的長度有關 {assert(arr!=NULL); if(k==length)//遞歸結束的條件{printf("{ ");for(int i=0;i<length;i++){if(arr[i]==1){printf("%c ",brr[i]);}}printf("}");printf("\n"); }else{arr[k]=0;//集合中的第i個元素沒有包含,也就是(No)Ziji(brr,k+1,length); //進行沒有被包含(No)的遞歸arr[k]=1;//集合中的第i個元素被包含(Yes)Ziji(brr,k+1,length);//然后進行被包含下(Yes)的遞歸} } int main() {char str[]="ABC";int lenth=strlen(str);Ziji(str,0,lenth);return 0; }?綜上所述呢,我們可以得到一個含有N個元素的集合,它的所有子集的個數為N!個。
?大家現在結合著代碼是不是理解了此算法的遞歸操作呢?
如果沒理解我再給大家講一下非遞歸的算法,我覺得這種辦法挺好運的,這種辦法就是用C++中的位運算符來進行操作的,上面我們可以得出一個含有N個元素的集合,它的所有子集的個數為N!個,于是我們對其進行移位操作。
如集合A={a,b,c},其子集個數為8;對于任意一個元素,在每個子集中,
要么存在,要么不存在,對應關系是:
a->1或a->0
b->1或b->0
c->1或c->0
映射為子集:
(a,b,c)
(1,1,1)->(a,b,c)
(1,1,0)->(a,b ? )
(1,0,1)->(a, ? c)
(1,0,0)->(a ? ? ?)
(0,1,1)->( ? b,c)
(0,1,0)->( ? b ? )
(0,0,1)->( ? ? ?c)
根據以上規律我們可以得出拿一個整形值與一個數進行&操作可以得到我們所需要的子集。進行再一步的觀察我們可以看出這個數就是1,每次進行&之后,讓這個數進行左移(<<)i位,這個i的初始為0,結束為N,所以我們就可以求出我們所需要的結果,具體代碼如下:
#include<stdio.h> #include<assert.h> #include<string.h> int main() {char arr[]="ABC";int a=strlen(arr);int thm=1<<a;//含有N個元素的子集共有N!個for(int i=0;i<thm;i++){for(int j=0;j<a;j++)if(i&1<<j)//每次要對其進行左移操作,然后又i進行&{printf("%c",arr[j]); }printf("\n");}return 0; }以上的內容就是我對一個集合中求子集問題的所有理解,希望它可以幫助大家。如有錯誤,請大家評論指正。
?
?
?
總結
以上是生活随笔為你收集整理的回溯法经典算法 求集合中所有的子集的全部內容,希望文章能夠幫你解決所遇到的問題。