全排列,迴圈,交換,遞迴,回溯
阿新 • • 發佈:2021-01-03
排列問題
關鍵詞:迴圈,交換,遞迴,回溯
舉例
eg: {1, 2, 3}的全排列是多少?
利用高中全排列的知識,做出如下解釋:第一個位置可放3個元素,第二個位置還剩下2個元素可放,最後放一個元素,得到3 * 2 * 1 = 6 种放法。
那麼如何用程式碼表示上面的放法呢?
第一個位置三种放法,首先怎麼得到三個資料呢?
第一次遍歷,自己和自己交換,第二次遍歷,自己和第二個數交換,第三次,自己和第三個數交換。
int k = 0;
int m = 2;
for(int i = k; i <= m; i++) {
swap(list[i], list[k] ;
}
/* 這裡我們看到要想第一次得到自己,第二次得到第二個數,
第三次得到第三個數的前提是,每一次交換必須保證是{1,2,3}的初始態,
下面解釋怎麼復原*/
那麼第二個位置兩種方法怎麼來表示呢,和第一個位置一樣一定也是迴圈,聰明的小夥伴一定發現了,下標起始位置不同。問題又來了,第一個位置進行了第一次交換後第二個位置怎麼進行第一次交換?
遞迴鴨!
Perm(list, k + 1, m)
那麼每次怎麼進行復原,比如{1, 3, 2},下一個全排列是多少,下一個狀態一定是{2, 1, 3},首先我們得還原為{1,2,3},怎麼還原?第三次迴圈結束條件不滿足,不進入迴圈體,返回上一級,我們剛剛怎麼交換的就怎麼交換回來,再swap()一下。這就是回溯的體現。
swap(list[k],list[i]);
那麼到這裡,我們其實已經得到程式碼的核心部分,剩下的部分就是出口問題,我們想想,最後一個數用不用交換???是不是不用,倒數第二個數的交換,是不是就已經完成了,所以出口就是倒數第二個數完成後,準備進入下一次交換時,遞迴返回。
程式碼如下:
template <class Type>
void Perm(Type list[], int k, int m) {
if( k == m) {
for(int i = 0; i <= m; i++) {
cout << list[i];
}
cout << endl;
} else {
for(int i = k; i <= m; i++) {
swap(list[i], list[k]);
Perm(list, k + 1, m);
swap(list[i], list[k]);
}
}
}
int main() {
int list[3] = {1,2,3};
Perm(list, 0, 2);
return 0;
}
用腦子跑一下:
{1,2,3}
{1,3,2}
{2,1,3}
{2,3,1}
{3,2,1}
{3,1,2}
電腦結果如下: