關於遞歸和回溯的說明以及8皇后問題的遞歸流程分析

回溯是一種思維,而遞歸(迭代)是一種實現回溯思維的編程方法;


回溯 : 是一種試錯的思維方法,對於一些不能夠通過表達式或者解析式描述的問題,或者有表達式但是實現起來相當複雜的一些算法,就是用於回溯法,特別是一些深度優先搜索(所搜樹)等等問題,比如下面要說的8皇后問題;


遞歸一般解釋就是自己調用自己,他的實現是通過系統的堆棧的完成的,每調用一次自己,當前參數將會被保存到堆棧當中,知道當前級函數返回,就會調用上一級的堆棧數據來繼續計算。


八皇后請自行百度


解決八皇后的思路 (n皇后思路類似):

1) 首先將第一個皇后放置在第一行的第一列中;


2) 然後在將第二個皇后放置在第二行中,由於第二行有8個位置可行,所以通過for循環來便利8個位置,直至找到合適的位置並將記錄該位置;


3) 然後將第三個皇后放置在第3行中,同樣也遍歷各個列的位置


以此類推 。。。。


4) 當發現其中第n個皇后遍歷完成n行的所有列都不符合,此時我將返回上行,改變上一行皇后的位置,然後繼續往下遍歷,直到遍歷找到一組真確的解


5) 如果找到了一組解之後,即每一行中都存在一個皇后,此時輸出當前成立的一組數列,並且將最後一行的換後位置清零,並且認爲的將上一行的皇后位置往後在移動一位,再繼續遍歷,這樣可以遍歷完成所有的可能結果



根據上述的思路,皇后的位置類似於堆棧的操作,如果發現一個皇后符合條件,則將皇后的位置壓棧,如果發現遍歷完成所有的列後沒有,則出棧並改變上一個棧的位置


代碼段如下 :


#include "stdafx.h"
#include "math.h"
#include "stdio.h"
#define cloum 8
#define row 8
#define NumbersQ 8
int index = 0;
short EGITVALUE[NumbersQ+1];
void showe(){
printf("//------------------------------------//\n");
printf("the number of qeun is : %d\n" , index);
short i = 0;
short j = 0;
for(i = 1; i < cloum+1 ; i ++){
for(j = 1; j < cloum+1 ; j ++){
if(EGITVALUE[i] == j) printf("%c " , '#');
else printf("%c " , '0');
}
printf("\n");
}
}
int IsMach(int k){
int i = 1;
while(i<k){
if(EGITVALUE[i] == EGITVALUE[k]) return 0;
if(abs( EGITVALUE[k] - EGITVALUE[i]) == abs(k -i)) return 0;
i++;
}
return 1;
}
//----------------------------------------------
//遞歸
int quen2(int n){
short j = 0;
for(j = 1; j <= NumbersQ ; j++){
EGITVALUE[n] = EGITVALUE[n] + 1;
if(n == 8 && IsMach(n)){
showe();
index = index + 1;
EGITVALUE[n] = 0;
return 0;
}
else if(IsMach(n)){
quen2(n + 1);
}
}
EGITVALUE[n] = 0;
return 0;
}
//----------------------------------------------
void quen1(){
int k = 1;
while(k >= 1){
while(EGITVALUE[k] < cloum){
EGITVALUE[k] = EGITVALUE[k] + 1;
if(k == 8 && IsMach(k)){
index = index + 1;
showe();
}
else if(IsMach(k)){
k++;
}
}
EGITVALUE[k] = 0;
k--;
}
}
void main()
{
quen2(1);
while(1);
}


pc端的遞歸過程 :

對於遞歸的編寫思路是這樣的,他會與非遞歸的思想稍微有一些不一樣:


具體如程序代碼所示 :


1、調用quen2(1);此時表示我輸進去的是第一個皇后,即首先找到第一個皇后的位置


2、進入quen2(n)函數之後,一個皇后n,(這個n也代表此皇后所在的行數,兩個變量在數值上是一樣的)在第n行中有8個位置可以放置,具體放置在哪一個位置,則通過全遍歷來確定。所以for循環是從1 : 8次的8次循環。


3、EGINAVLUE[n] ; 他表示第n個皇后在第n行第EGINAVLUE[n] 列;假設此時的n = 1 ; EGINAVLUE[n] = 0;表示了我將第一個皇后放置在了第1行第0列上;由於在回溯過程中會涉及到當前值的自增過程,所以此處都已第一行第一列作爲起始位置,所以EGINAVLUE[n] = EGINAVLUE[n]  + 1; 表示當前我是將第一個皇后放置到了第一行第一列的位置。


4、IsMach函數主要試判斷當前位置是否合法,他的判斷也是比較簡單,思路大概是,我將當前皇后所在的行數(次行數是和n值在數值上相等的)輸入到IsMach函數中,那麼該函數會將該位置與當前皇后行數之間的各行數的皇后做判斷,以判斷該位置是否合法。


5、此時判斷當前n == 8 && IsMach(n);此條件是判斷我是否已經放置到了最後一個皇后,並且該位置是合法的,此時輸出這一組解,並將當前皇后的位置清零EGINAVLUE[n] = 0;這句話後面再解釋


6、如果5的條件不滿足,但是滿足IsMach(n);則說明,該位置是符合條件的,皇后能夠放置在這個位置上,由於在第三步的EGINAVLUE[n] = EGINAVLUE[n]  + 1; 操作,所以EGINAVLUE[n],就相當於記錄了當前皇后的位置。於是我在重新調用quen(n+1);表徵我去尋找下一個皇后的位置


7、調用quen(n+1)時,系統將n的值存入到堆棧當中,並將n+1的值傳遞到quen2(n+1)中繼續執行


8、此時皇后已經到了n+1個;對於當前的話就是第二個皇后,n = 2;同樣n= 2 的這個皇后需要在第2行中遍歷所有的位置以確定他的位置


。。。。。。以此類推


9、現在假設n = 7;並且前面6個皇后的位置都已經找到了;在尋找第7個皇后的位置時;for在遍歷8列中沒有一個位置符合條件,所以結束for循環,並且執行EGINAVLUE[n] = 0;即將第7個皇后的位置清零,一遍下一次來遍歷


10、return ; 返回後函數會繼續執行上一次調用的quen(n)的函數,只不過此時的n值是6,即上一次的壓棧值,


11、繼續運行for循環,發現此時for循環的條件滿足,並且執行EGINAVLUE[n]  = EGINAVLUE[n]  + 1; 相當於第7行的皇后的位置不可用,所以我會退到上一行,並將上一行的換後位置向後移動一個。


12、之後繼續往下判斷,如果當前(n = 6)的位置符合條件,將會在去查找n = 7下一行的位置查詢


13、這種操作會一直遞歸下去,知道第一個皇后被移動到最後一個位置,整個函數將會結束


當然 這個最好的觀測他的運行過程是通過c語言一步一步調試,這樣是最清楚的




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章