程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C說話完成的分列組合成績的通用算法、處理辦法

C說話完成的分列組合成績的通用算法、處理辦法

編輯:關於C++

C說話完成的分列組合成績的通用算法、處理辦法。本站提示廣大學習愛好者:(C說話完成的分列組合成績的通用算法、處理辦法)文章只能為提供參考,不一定能成為您想要的結果。以下是C說話完成的分列組合成績的通用算法、處理辦法正文


雖然分列組合是生涯中常常碰到的成績,可在法式設計時,不深刻思慮或許經歷缺乏都讓人無從下手。因為分列組合成績老是先取組合再分列,而且純真的分列成績絕對簡略,所以本文僅對組合成績的完成停止具體評論辯論。以在n個數當選取m(0<m<=n)個數為例,成績可分化為:

1. 起首從n個數當選取編號最年夜的數,然後在剩下的n-1個數外面拔取m-1個數,直到從n-(m-1)個數當選取1個數為止。

2. 從n個數當選取編號次小的一個數,持續履行1步,直到以後可選編號最年夜的數為m。

很顯著,上述辦法是一個遞歸的進程,也就是說用遞歸的辦法可以很清潔利索地求得一切組合。

上面是遞歸辦法的完成:

/// 求從數組a[1..n]中任選m個元素的一切組合。
/// a[1..n]表現候全集,n為候全集年夜小,n>=m>0。
/// b[1..M]用來存儲以後組合中的元素(這裡存儲的是元素下標),
/// 常量M表現知足前提的一個組合中元素的個數,M=m,這兩個參數僅用來輸入成果。
void combine( int a[], int n, int m,  int b[], const int M )
{
 for(int i=n; i>=m; i--)   // 留意這裡的輪回規模
 {
  b[m-1] = i - 1;
  if (m > 1)
   combine(a,i-1,m-1,b,M);
  else                     // m == 1, 輸入一個組合
  {  
   for(int j=M-1; j>=0; j--)
    cout << a[b[j]] << " ";
   cout << endl;
  }
 }
}

由於遞歸途序都可以經由過程引入棧,用回溯轉化為響應的非遞歸途序,所以組合成績又可以用回溯的辦法來處理。為了便於懂得,我們可以把組合成績化歸為圖的途徑遍歷成績,在n個數當選取m個數的一切組合,相當於在一個如許的圖中(上面以從1,2,3,4中任選3個數為例解釋)求從[1,1]地位動身達到[m,x](m<=x<=n)地位的一切途徑:

1  2  3  4
    2  3  4
        3  4

上圖是截取n×n右上對角矩陣的前m行組成,假如把矩矩中的每一個元素看做圖中的一個節點,我們請求的一切組合就相當於從第一行的第一列元素[1,1]動身,到第三行的隨意率性一列元素作為停止的一切途徑,劃定只要相鄰行之間的節點,而且下一行的節點必需處於上一行節點左面才有途徑相連,其他情形都無途徑相通。明顯,任一途徑經由的數字序列就對應一個相符請求的組合。

上面長短遞歸的回溯辦法的完成:
/// 求從數組a[1..n]中任選m個元素的一切組合。
/// a[1..n]表現候全集,m表現一個組合的元素個數。
/// 前往一切組合的總數。
int combine(int a[], int n, int m)
{  
 m = m > n ? n : m;

 int* order = new int[m+1];   
 for(int i=0; i<=m; i++)
  order[i] = i-1;            // 留意這裡order[0]=-1用來作為輪回斷定標識
 
 int count = 0;                               
 int k = m;
 bool flag = true;           // 標記找到一個有用組合
 while(order[0] == -1)
 {
  if(flag)                   // 輸入相符請求的組合
  {  
   for(i=1; i<=m; i++)                   
    cout << a[order[i]] << " ";
   cout << endl;
   count++;
   flag = false;
  }

  order[k]++;                // 在以後地位選擇新的數字
  if(order[k] == n)          // 以後地位已有數字可選,回溯
  {
   order[k--] = 0;
   continue;
  }    
 
  if(k < m)                  // 更新以後地位的下一名置的數字         
  {
   order[++k] = order[k-1];
   continue;
  }
 
  if(k == m)
   flag = true;
 }

 delete[] order;
 return count;
}

上面是測試以上函數的法式:

int main()
{
 const int N = 4;
 const int M = 3;
 int a[N];
 for(int i=0;i<N;i++)
  a[i] = i+1;

 // 回溯辦法
 cout << combine(a,N,3) << endl;

 // 遞歸辦法
 int b[M];
 combine(a,N,M,b,M);

 return 0;
}

由上述剖析可知,處理組合成績的通用算法不過乎遞歸和回溯兩種。在針對詳細成績的時刻,由於遞歸途序在遞歸層數上的限制,關於年夜型組合成績而言,遞歸不是一個好的選擇,這類情形下只能采用回溯的辦法來處理。

n個數的全分列成績絕對簡略,可以經由過程交流地位順次列舉來完成。STL供給了求某個序列下一個分列的算法next_permutation,其算法道理以下:
1. 從以後序列最尾端開端往前尋覓兩個相鄰元素,令後面一個元素為*i,後一個元素為*ii,且知足*i<*ii;

2. 再次從以後序列末尾開端向前掃描,找出第一個年夜於*i的元素,令為*j(j能夠等於ii),將i,j元素對換;

3. 將ii以後(含ii)的一切元素倒置順序,如許所得的分列即為以後序列的下一個分列。

其完成代碼以下:

template <class BidirectionalIterator>
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last)
{
  if (first == last) return false;   // 空範圍
  BidirectionalIterator i = first;
  ++i;
  if (i == last) return false;       // 只要一個元素
  i = last;                          // i 指向尾端
  --i;

 for(;;)
 {
  BidirectionalIterator ii = i;
  --i;
  // 以上,鎖定一組(兩個)相鄰元素
  if (*i < *ii)                     // 假如前一個元素小於後一個元素
  {
   BidirectionalIterator j = last;  // 令 j指向尾端
   while (!(*i < *--j));            // 由尾端往前找,直到趕上比 *i 年夜的元素
   iter_swap(i, j);                 // 交換 i, j
   reverse(ii, last);               // 將 ii 之後的元素全體逆向重排
   return true;
  }
  if (i == first)                   // 進行至最後面了
  {
   reverse(first, last);            // 全體逆向重排
   return false;
  }
 }
}

上面法式演示了應用next_permutation來求取某個序列全分列的辦法:

int main()
{
 int ia[] = {1,2,3,4};
 vector<int> iv(ia,ia+sizeof(ia)/sizeof(int));

 copy(iv.begin(),iv.end(),ostream_iterator<int>(cout," "));
 cout << endl;
 while(next_permutation(iv.begin(),iv.end()))
 {
  copy(iv.begin(),iv.end(),ostream_iterator<int>(cout," "));
  cout << endl;
 }

 return 0;
}

留意:下面法式中初始序列是按數值的從小到年夜的次序分列的,假如初始序列無序的話,下面法式只能求出從以後序列開端的後續部門分列,也就是說next_permutation求出的分列是按分列從小到年夜的次序停止的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved