【演算法總結-排列組合與子集問題】排列組合與子集問題

來源:互聯網
上載者:User

1.組合問題:

    問題描述:對於一組各不相同的數字,從中任意抽取1-n個數字,構成一個新的集合。求出所有的可能的集合。例如,對於集合{1,2,3},其所有子集為{1},{2},{3},{1,2},{1,3},{2,3}{1,2,3}, 給定一個數組(元素各不相同),求出數組的元素的所有非空組合(即數組的所有非空子集)

    解法一:位向量法。用一個輔助數組表示各個元素的狀態。1表示在集合中,0表示不在數組中。遞迴地求解所有的子集。
            演算法描述如下://這裡的演算法對空集也輸出了,可修改之使得只輸出非空集合。

            void getSubSet(int *a,int *b,int n,int k){                if(k==n){                    for(int i = 0;i < n;i++){                        if(i == 0){                            printf("{ ");                        }else if(i==(n-1)){                            printf(" }\n");                        }                        if(b[i]){                            printf("%d, ",a[i]);                        }                    }                    return ;                }                b[k] = 1;                getSubSet(a,b,n,k+1);                b[k] = 0;                getSubSet(a,b,n,k+1);            }

        解法二:位元影像的思想。思路類似與解法一位向量。用n個位來儲存相應的元素是否在集合中,如果在集合中,相應位為1.否則為0;
            程式碼範例://註:這裡用的是位元組而不是c++中的bitmap

            void print_subset(int n,int s){                printf("{");                for(int i = 0;i<n;i++){                    if(s&(1<<i)) printf("%d ",i);//或者a[i]                }                printf("}\n");            }            void subset(int n){                for(int i= 0;i<(1<<n);i++){                    print_subset(n,i);                }            }

        只需要調用subset(n)即可輸出1->n個數位所有組合。或者修改輸出部分為輸出一個特定集合的組合。

2.。排列問題。

    給定一組不相同的數字。求出這n個數位各種排列形式。稱為排列問題。

    解法一:暴力搜尋,對於一個全排列問題,相當於搜尋一個具有n個n-1叉數的深林。暴力搜尋之,得到所有的全排列形式。代碼如下:

#include <stdio.h>#include <stdlib.h>void output(int *a,int n){for(int i = 0;i<n;i++){printf("%d ",a[i]);}printf("\n");}void perm(int *a,int *b,int n,int k){int i,j;if(n==k){output(b,n);}else{for( i = 0;i < n;i++){int flag = 1;for( j = 0;j < k;j++){if(b[j] == a[i]){flag = 0;}}if(flag){b[k] = a[i];perm(a,b,n,k+1);}}}}int main(){int *a =new int[3];int *b =new int[3];for(int i = 0;i<3;i++){a[i] = i+1;b[i] = i+1;}perm(a,b,3,0);}

        解法二:類比回溯法產生排列的過程,對於已知的一個序列,如果交換其中兩個元素的,會得到新的序列。思路類似於產生組合問題。演算法描述如下:

        void permutation(int *a, int n,int k){            if(n==k){                printf("{");                for(int i = 0;i<n;i++){                    printf("%d ",a[i]);                }                printf("}\n");                return ;            }            for(int i = k;i<n;i++){                swap(&a[i],&a[k]);                permutation(a,n,k+1);                swap(&a[i],&a[k]);            }        }

        解法三:c++ 中STL中next_permutation()方法。注意這種方法要得到所有的排列,需要原始數組為遞增有序的,可先對其qsort()

        do{            printf("{");             for(int i = 0;i<N;i++){                printf("%d ",a[i]);            }            printf("}\n");        }while(next_permutation(a,a+N));

//TODO 全排列中有重複元素的演算法總結

3.笛卡爾積問題。

@xuzuning

    問題描述:笛卡爾(Descartes)乘積又叫直積。設A、B是任意兩個集合,在集合A中任意取一個元素x,在集合B中任意取一個元素y,組成一個有序對(x,y),
       把這樣的有序對作為新的元素,他們的全體組成的集合稱為集合A和集合B的直積,記為A×B,即A×B={(x,y)|x∈A且y∈B}。n對集合的笛卡爾積由此遞迴定義得到。

<?php           /*            * 笛卡爾(Descartes)乘積又叫直積。設A、B是任意兩個集合,在集合A中任意取一個元素x,在集合B中任意取一個元素y,組成一個有序對(x,y),            * 把這樣的有序對作為新的元素,他們的全體組成的集合稱為集合A和集合B的直積,記為A×B,即A×B={(x,y)|x∈A且y∈B}。            * @author xuzuning            */            function Descartes() {                $t = func_get_args();                if(func_num_args() == 1) {                   return call_user_func_array( __FUNCTION__, $t[0] );                }                $a = array_shift($t);                if(! is_array($a)) {                    $a = array($a);                }                $a = array_chunk($a, 1);//目的是分解成array(a)這樣的形式,便於後面的合并                do {                    $r = array();                    $b = array_shift($t);                    if(! is_array($b)) $b = array($b);                    foreach($a as $p)                        foreach(array_chunk($b, 1) as $q)                            $r[] = array_merge($p, $q);                    $a = $r;                }while($t);                return $r;            }            $arr = array(               array('a1','a2',),               'b',               array('c1','c2',),               array('d1','d2','d3')            );            $r = Descartes( $arr );        ?>

上述求全排列和子集的方法,稱為回溯法。對於回溯法,有一個基本的架構(模式):

void backTrack(int n,int k){    if(符合條件){        輸出;        return;    }    else{        執行代碼;        backTrack(n,k+1);        代碼回溯;    }          }

    利用這個模式可以寫出例如組合,全排列,子集,八皇后等問題。

八皇后問題的解法:http://blog.csdn.net/ohmygirl/article/details/6924229。比較經典,不需過多解釋。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.