遞歸是一種數學上分而自治的思想。 A、將原問題分解爲規模較小的問題進行處理 分解後的問題與原問題類型徹底相同,當規模較小。 經過小規模問題的解,可以輕易求得原生問題的解 B、問題的分解時有限的 當邊界條件不能知足時,分解問題(繼續遞歸) 當邊界條件知足時,直接求解(遞歸結束)
遞歸模型的通常表示法:
遞歸在程序設計中的應用 遞歸函數: 函數體中存在自我調用的函數 遞歸函數必須有遞歸出口(邊界條件) 函數的無限遞歸將致使程序崩潰 使用遞歸函數時不要陷入遞歸函數的執行細節,應首先創建遞歸模型和確立邊界條件。
int sum(unsigned int n) { int ret; if(n > 1) { ret = n + sum(n - 1); } else if(n == 1) { ret = 1; } return ret; }
unsigned int Fibonacci(unsigned int n) { unsigned int ret ; if(2 < n) { ret = Fibonacci(n -1) + Fibonacci(n -2); } else if((n == 1) || (n == 2)) { ret = 1; } return ret; }
int _strlen_(const char* s) { int ret = 0; if(*s != '\0') { ret = 1 + _strlen_(s+1); } else { ret = 0; } return ret; }
代碼簡化:
unsigned int _strlen_(const char* s) { return s?((*s)?(1 + _strlen_(s + 1)):0):0; }
typedef struct { int data; Node* next; }Node; Node* reverse(Node* list) { Node* ret = NULL; if(list == NULL || list->next == NULL) { ret = list; } else { Node* guard = list->next; ret = reverse(list->next); guard->next = list; list->next = NULL; } return ret; }
typedef struct { int data; Node* next; }Node; Node* merge(Node* list1, Node* list2) { Node* ret = NULL; if(NULL == list1) { ret = list2; } else if(NULL == list2) { ret = list1; } else if(list1->data < list2->data) { list1->next = merge(list1->next,list2); ret = list1; } else { list2->next = merge(list2->next, list1); ret = list2; } return ret; }
漢諾塔問題: A、將木塊藉助B柱由A柱移動到C柱 B、每次只能移動一塊木塊 C、小木塊只能出如今大木塊之上
漢諾塔問題的解決方案:
A、將n-1個木塊藉助C柱由A柱移動到B柱
B、將最底層的木塊直接移動到C柱
C、將n-1個木塊藉助A柱由B柱移動到C柱
算法
/********************************* * n:木塊的數量 * A:A柱 * B:B柱 * C:C柱 * 漢諾塔問題:將n個木塊從A柱藉助B柱移動到C柱 * ******************************/ void HanoiTower(int n, char A, char B, char C) { if(n == 1) { cout << A << "-->" << C << endl; } else { //將A柱上的n-1個木塊藉助C柱移動到B柱 HanoiTower(n-1, A, C, B); //將A柱上的木塊,直接移動到C柱 HanoiTower(1, A, B, C); //將B柱上的n-1個木塊藉助A柱移動到C柱 HanoiTower(n-1, B, A, C); } }
void permutation(char* s, char* ret) { if('\0' == *s) { cout << ret << endl; } else { int len = strlen(s); for(int i = 0; i < len; i++) { if(0 == i || (s[0] != s[i])) { swap(s[0], s[i]); permutation(s+1, ret); swap(s[0], s[i]); } } } }
程序運行後有一個特殊的內存區域(棧)供函數調用使用: A、用於保存函數中的實參、局部變量、臨時變量等 B、從起始地址開始往一個方向增加 C、有一個專用指針標識當前已用內存的頂部 程序中的棧區:
逆序打印單鏈表中的偶數結點:
數組
typedef struct { int data; Node* next; }Node; void r_print_even(Node* list) { if(NULL !=list) { r_print_even(list->next); if(list->data %2 == 0) { cout << list->data << endl; } } }
回溯算法實際上一個相似枚舉的搜索嘗試過程,主要是在搜索嘗試過程當中尋找問題的解,當發現已不知足求解條件時,就「回溯」返回,嘗試別的路徑。回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步從新選擇,這種走不通就退回再走的技術爲回溯法,而知足回溯條件的某個狀態的點稱爲「回溯點」。數據結構
回溯算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。八皇后問題就是回溯算法的典型,第一步按照順序放一個皇后,而後第二步符合要求放第2個皇后,若是沒有位置符合要求,那麼就要改變第一個皇后的位置,從新放第2個皇后的位置,直到找到符合條件的位置就能夠了。回溯在迷宮搜索中使用很常見,就是這條路走不通,而後返回前一個路口,繼續下一條路。回溯算法說白了就是窮舉法。不過回溯算法使用剪枝函數,剪去一些不可能到達最終狀態(即答案狀態)的節點,從而減小狀態空間樹節點的生成。回溯法是一個既帶有系統性又帶有跳躍性的的搜索算法。它在包含問題的全部解的解空間樹中,按照深度優先的策略,從根結點出發搜索解空間樹。算法搜索至解空間樹的任一結點時,老是先判斷該結點是否確定不包含問題的解。若是確定不包含,則跳過對以該結點爲根的子樹的系統搜索,逐層向其祖先結點回溯。不然,進入該子樹,繼續按深度優先的策略進行搜索。回溯法在用來求問題的全部解時,要回溯到根,且根結點的全部子樹都已被搜索遍才結束。而回溯法在用來求問題的任一解時,只要搜索到問題的一個解就能夠結束。這種以深度優先的方式系統地搜索問題的解的算法稱爲回溯法,它適用於解一些組合數較大的問題。`ide
在8X8的國際象棋棋盤上,有8個皇后,每一個皇后佔一個格子,要求皇后之間不會出現相互***的現象(任意兩個皇后不能處在同一行、同一列或者同一對角線上)。
棋盤的定義:
二維數組(10X10),0表示位置爲空,1表示皇后,2表示邊界。
位置的定義:函數
struct Pos { int x; int y; };
方向定義: 水平左方向(-1, 0) 水平右方向(1, 0) 垂直上方向(0, 1) 垂直下方向 (0, -1) 左上對角線方向 (-1, 1) 左下對角線方向 (-1, -1) 右下對角線方向 (1, -1) 右上對角線方向 (1, 1)
template <int SIZE> class QueueSolution : public Object { protected: enum{N = SIZE + 2};//棋盤大小 struct Pos : public Object { Pos(int px = 0, int py = 0):x(px),y(py){} int x; int y; bool operator==(const Pos& other) { return (this->x = other.x && this->y == other.y); } }; int m_chessBoard[N][N];//棋盤數據 Pos m_direction[3];//方向數據 LinkedList<Pos> m_solution;//皇后的位置 int m_count;//解決方案的數量 void init() { m_count = 0; //初始化棋盤邊界 for(int x = 0; x < N; x += (N-1)) { for(int y = 0; y < N; y++) { m_chessBoard[x][y] = 2;//垂直邊界 m_chessBoard[y][x] = 2;//水平邊界 } } //初始化棋盤 for(int x = 1; x <= SIZE; x++) { for(int y = 1; y <= SIZE; y++) { m_chessBoard[x][y] = 0; } } //左下角對角線方向 m_direction[0] = Pos(-1, -1); //垂直向下方向 m_direction[1] = Pos(0, -1); //右下角對角線方向 m_direction[2] = Pos(1, -1); } //打印棋盤 void printBoard() { for(m_solution.move(0); !m_solution.end(); m_solution.next()) { cout << "(" << m_solution.current().x << "," << m_solution.current().y<< ") "; } cout << endl; for(int x = 0; x < N; x++) { for(int y = 0; y < N; y++) { switch (m_chessBoard[x][y]) { case 0: cout << " "; break; case 1: cout << "Q"; break; case 2: cout << "#"; break; } } cout << endl; } } bool check(int x, int y, int direction) { bool flag = true; do { x += m_direction[direction].x; y += m_direction[direction].y; flag = flag && (m_chessBoard[x][y] == 0); }while(flag); return (m_chessBoard[x][y] == 2); } void run(int y) { if(y < SIZE) { for(int x = 1; x < SIZE; x++) { if(check(x,y,0) && check(x,y,1)&&check(x,y,2)) { m_chessBoard[x][y] = 1; m_solution.insert(Pos(x, y)); run(y+1); m_chessBoard[x][y] = 0; m_solution.remove(m_solution.length() - 1); } } } else { m_count++; printBoard(); } } public: QueueSolution() { init(); } void run() { run(1); cout << "Total:" << m_count << endl; } };