對輸入的正整數n,輸出{0,1,...,n-1}的所有子集。例如,輸入3時,輸出如下:
{},{0},{1},{0,1},{2},{0,2},{1,2},{0,1,2}
這個題目可以考慮用二進制的方法來反映排列組合(輸入數字3對應3位二進制數,3位二進制數共有8種寫法,而包含三個元素的集合的排列組合方式也有8種),就直接拿下面的這個表格來舉例子:
當二進制數為000的時候,默認為空集
當最低位為1其余高位為0的時候,代表當前集合為{0};
當次地位為1其余位為0的時候,代表當前集合為{1};
當高位為1其余低位0的時候,代表當前集合位{2};
以上三種可以任意組合,比如011代表{0,1}、111代表{0,1,2}、010代表{1};
而且這里值得注意的是,二進制對應的數可以組合成有順序的序號
子集 | 序號trans | 對應的二進制數 |
---|---|---|
{} | 0 | 000 |
{0} | 1 | 001 |
{1} | 2 | 010 |
{0,1} | 3 | 011 |
{2} | 4 | 100 |
{0,2} | 5 | 101 |
{1,2} | 6 | 110 |
{0,1,2} | 7 | 111 |
直接上代碼:
int main(){ int num;//這個集合有多少個數 scanf("%d",&num); int cnt=(int)pow(2, num)-1;//實現序號的對應 for(int i=0;i<=cnt;i++){ printf("{"); int trans=i;//當前的數字 int set_num=0;//集合中需要填寫的數字 int comma_sign=0;//逗號 while (trans) {//根據當前的數字求解其二進制,根據二進制來求集合 if(trans%2==1){ if(!comma_sign){ printf("%d",set_num); ++comma_sign; } else{ printf(",%d",set_num); ++comma_sign; } } ++set_num; trans/=2; } if(i!=cnt) printf("},"); else printf("}"); // // if(!((i+1)%5)) // printf("\n"); } printf("\n"); }
為了解決集合中應該寫入數字幾的問題(比如集合中有1,2,3,此時當前子集是幾個數字,數字是多少的子集組合),這里引入了數字set_num=0變量,當檢測到低位存在數字1的時候,先輸出當前set_num的值0,然后令set_num自加
當再次檢測到次低位存在1到時候,輸出set_num當前的值1,並再次令set_num自加,這是為了如果再次檢測到高位為1的存在的時候可以輸出2
這樣說很抽象,我舉個例子:
當當前序號為7的時候:
7首先會進入while判斷,對7進行2的取余操作得1,那么意味着有低位存在,也就是當前子集當中含0元素,打印當前set_num,接着令set_num自加得1備用
對7進行除2操作(繼續轉2進制)得3賦給原數
3再次進入到while判斷,對3進行2的取余操作得1,那么意味着有次低位存在,也就是當前子集當中含1元素,打印當前set_num,接着令set_num自加備用
。。。
重寫了個簡單的版本
int n=3; int combination=1; for(int i=1;i<=n;i++) combination*=2; combination-=1;//根據網上的公式求解子集個數為2^n -1 for(int i=0;i<=combination;i++){//每一種編號都代表一種組合方式 /** 看那個表,每一個數字都對應一個二進制,例如7用輾轉相處法表示111,每出現一個1,就讓記錄的k自加 這樣,k由0,變到1,再變到2,則出現了{0,1,2} 當combination等於2的時候,運用輾轉相除法先得到0,那么跳過if(num%2==1),直接++k,由於當前num不等於0 再進行一輪輾轉,下一次再輾轉求余得到1,打印 所以結果為{1} 當combination等於3的時候,在while中輾轉求余先得到1,此時打印出sign為{0,當前num為1,sign++ 接着再進入輾轉,符合if條件,打印sign為{0 1 ...最后得出結果為{0,1} */ int sign=0; printf("{"); int num=i; while(num!=0){ if(num%2==1) printf("%d ",sign); sign++;//重要 num/=2; } printf("}"); }