1. 程式人生 > >第44課 遞歸的思想與應用(中)

第44課 遞歸的思想與應用(中)

!= 遞歸法 ati 恢復 直接 clu spa tex height

1. 單向鏈表的轉置

技術分享

【編程實驗】單向鏈表的轉置(Node* reverse(Node* list)

2. 單向排序鏈表的合並

技術分享

【編程實驗】單向排序鏈表的合並(Node* merge(Node* list1, Node* list2)

3. 漢諾塔問題

(1)遊戲規則

技術分享

  ①將木塊借助B柱由A柱移動C柱

  ②每次只能移動一個土塊

  ③只能出現小土塊在大木塊之上

(2)遞歸求解的問題分解

技術分享

  ①將n-1個木塊借助C柱由A移動到B柱。

  ②將最底層的唯一木塊直接移動到C柱

  ③將n-1個木塊借助A柱由B柱移動到C柱

【編程實驗】漢諾塔問題求解(void HanoiTower(int n, char a, char b, char c)

4. 全排列問題

技術分享

【編程實驗】全排列的遞歸解法(void permutation(char* s)

//main.cpp

#include <iostream>
#include <cstring>

using namespace std;

struct Node
{
    int value;
    Node* next;
};

//創建無表頭結點的單鏈表(己填充數據):v結點的值,len鏈表長度
Node* create_list(int v, int len)
{
    Node* ret = NULL;
    Node* slider = NULL;

    
for(int i=0; i<len; i++){ Node* node = new Node(); node->value = v++; node->next = NULL; //每創建好一個節點,slider指向這個節點 if( slider == NULL ){ slider = node; ret = node; }else{ slider->next = node; slider
= node; } } return ret; } void destroy_list(Node* list) { while(list){ Node* del = list; list = list->next; delete del; } } //打印鏈表的內容 void print_list(Node* list) { while(list){ cout << list->value << "->"; list = list->next; } cout << "NULL" << endl; } //單鏈表轉置(遞歸法) Node* reverse(Node* list) { //遞歸出口 if((list == NULL) || (list->next == NULL)){ return list; }else{ Node* guard = list->next; //指向反轉前的子鏈表第1個結點 Node* ret = reverse(list->next); //轉置子鏈表 guard->next = list; //反轉後,guard變成子鏈表的最後一個結點 list->next = NULL; return ret; } } //單向排序鏈表的合並 Node* merge(Node* list1, Node* list2) { //遞歸出口 if(list1 == NULL) return list2; if(list2 == NULL) return list2; //取出子鏈表的第1個結點並比較 if(list1->value < list2->value){ //由於當前list1結點的值較小,取出當前結點,其next //指向由該結點後繼的子鏈表和list2合並的新鏈表 list1->next = merge(list1->next, list2); return list1; }else{ //list2當前結點的值較小 list2->next = merge(list1, list2->next); return list2; } } //漢諾塔 void HanoiTower(int n, char a, char b, char c) { if(n == 1){ cout << a << "-->" << c << endl; }else{ HanoiTower(n-1, a, c, b); //將n-1塊,從a柱借c柱移動到b柱 HanoiTower(1, a, b, c); //將a柱剩下的最後一塊移到c柱上 HanoiTower(n-1, b, a, c); //將b柱中的n-1塊借助a柱移到c柱。 } } //全排列 void permutation(char* s, char* e) { if(*s == \0) { cout << e << endl;//打印出全排列好的字符串 }else{ int len = strlen(s); //如將abc //1.第1輪:將abc變成以a開頭,然後全排列bc //2.第2輪:將abc變成以b開頭,然後全排列ac //3.第3輪:將abc變成以c開頭,然後全排列ba for(int i=0; i<len; i++){ if((i == 0) || (s[0] != s[i])){ //去重復,如aba就不交換兩個a swap(s[0], s[i]); //以s[0]開頭(每輪會變化),全排列其後的子串。 permutation(s+1, e); //全排列子串 swap(s[0], s[i]); //恢復為原來的串,如abc,以便下一輪全排列。 } } } } int main() { //測試:單鏈表轉置 Node* list = create_list(1, 10); print_list(list); list = reverse(list); print_list(list); destroy_list(list); //測試:單向排序鏈表的合並 Node* list1 = create_list(1,5); Node* list2 = create_list(3,7); print_list(list1); print_list(list2); Node* ret = merge(list1, list2); print_list(ret); destroy_list(ret); //測試:漢諾塔 HanoiTower(3, a, b, c); //測試:全排列 char s[]="aabc"; permutation(s, s); return 0; } /*測試結果: 1->2->3->4->5->6->7->8->9->10->NULL 10->9->8->7->6->5->4->3->2->1->NULL 1->2->3->4->5->NULL 3->4->5->6->7->8->9->NULL 1->2->3->3->4->4->5->5->6->7->8->9->NULL a-->c a-->b c-->b a-->c b-->a b-->c a-->c aabc aacb abac abca acba acab baac baca bcaa caba caab cbaa */

第44課 遞歸的思想與應用(中)