經典算法學習之分治法(以排列、組合程序為例)


分治法的思想:將原問題分解為幾個規模較小但類似於原問題的子問題,遞歸的求解這些子問題,然后再合並這些子問題的解來建立原問題的解。

分治法在每層遞歸是遵循的三個步驟:

(1)分解原問題為若干個子問題,這些子問題是原問題的規模較小的實例。

(2)解決這些子問題,隊規的求解各個子問題,當子問題規模足夠小的時候,直接求解。

(3)合並這些子問題的解構成原問題的解。

顯然歸並排序是一個非常經典規矩的分治法的例子,鑒於之前已經寫過一篇關於歸並排序的博文,這里不在使用歸並排序作為例子。

注意分治法的每一層遞歸中的第一步分解,可能產生兩個子問題(如歸並排序、二分查找等),也可能產生多個子問題(如排列、組合等),產生兩個子問題的時候當然比較容易理解,而產生多個子問題的時候需要使用羅列這些子問題。

下面就以排列和組合算法為例,介紹產生多個子問題的分治算法。

一、排列

 問題:輸入一個字符串,打印出該字符串中字符的所有排列。

 分析:利用分治法的思想,

(1)先將原問題分解,假如輸入的字符串長度是n,那么第一次選擇可能是第一個字符、也可能是第二個、。。。也可能是第n個,但是不管是哪一個,只要選出第一個字符,就可以在剩下的n-1個字符里面繼續選擇一個了,所以需要將原問題分解為n個子問題(每個子問題為第一步選擇的是i,然后再對除了i之外的字符進行全排列),到現在可以發現如果直接按照順序分解之后,對除了i之外的字符進行全排列,不是那么容易實現遞歸,於是想到將每個元素(包括第一個元素)都與第一個元素交換,然后分解成的子問題就是先將每個元素與第一個元素交換並選出,然后對第二個到最后的所有元素全排列。注意每次個子問題考慮完之后需要將交換的元素換回。

(2)利用遞歸解決每個子問題

(3)當所有問題都解決的時候,子問題的解組合起來就是原問題的解了

如:輸入字符串為abc ,排列函數為permutation()那么分解成的子問題為a+permutation(bc)、b+permutation(ac)、c+permutation(ab)

 1 #include "stdafx.h"
 2 #include<iostream>
 3 using namespace std;
 4 void print(char *str)
 5 {
 6     char *p=str;
 7     while(*p)
 8     {
 9         cout<<*p<<' ';
10         p++;
11     }
12 }
13 void bianli(char *str,int begin,int length)
14 {
15     char temp;
16     int i;
17     if(begin==length-1)
18     {
19         print(str);
20         cout<<endl;
21         return ;
22     }
23     //可以選取某一個值(包括begin自己)與begin的位置交換,然后對剩下的字符全排列
24     //所以對於每一個位置要么選擇先交換,然后遞歸,要么選擇不交換(即交換兩次)
25     for(i=begin;i<length;i++)
26     {
27         temp=str[begin];
28         str[begin]=str[i];
29         str[i]=temp;
30 
31         bianli(str,begin+1,length);
32 
33         temp=str[begin];
34         str[begin]=str[i];
35         str[i]=temp;
36     }
37 }
38 
39 int _tmain(int argc, _TCHAR* argv[])
40 {
41     char str[4]="123";
42     bianli(str,0,3);
43     return 0;
44 }

二、組合

問題:找出從自然數1、2、3。。。n中任取r個元素的所有組合

分析:

1、分解:與排列不同,組合里每個元素在一種只出現一次,所以並不需要交換元素,而是每次從n個數中按照某種順序取一個元素,然后考慮全面了即可,如每次取一個最大值,那么只要元素個數>k則是子問題的一種,剩下的思想和排列差不多。

 1 #include<iostream>
 2 using namespace std;
 3 int a[100];//用於存放組合的結果
 4 void zuhe(int n,int k)
 5 {
 6     for(int i=n;i>=k;i--)//順序選取組合中最大的數
 7     {
 8         a[k]=i;
 9         if(k>1)
10         {
11             zuhe(i-1,k-1);
12         }
13         else
14         {
15             for(int i=1;i<=a[0];i++)
16             {
17                 cout<<a[i]<<" ";
18             }
19             cout<<endl;
20         }
21     }
22 }
23 int main()
24 {
25     int n,k;
26     cin>>n>>k;
27     a[0]=k;
28     zuhe(n,k);
29     return 0;
30 }


免責聲明!

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



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