DFS和BFS淺談

深度優(yōu)先搜索(DFS)

深度優(yōu)先搜索俘侠,重點(diǎn)就在于“深度”一詞,不碰到死胡同就不回頭种玛。
深度優(yōu)先搜索是一種枚舉所有完整路徑以遍歷所有情況的搜索方法曹洽。
整個(gè)過程和出棧入棧的過程極為相似,可以使用棧來實(shí)現(xiàn)蹭沛。
首先先對問題進(jìn)行分析臂寝,得到岔路口和死胡同章鲤;再定義一個(gè)棧,以深度為關(guān)鍵詞訪問這些岔道口和死胡同咆贬,并將它們?nèi)霔0芑玻欢?dāng)離開這些岔道口和死胡同時(shí),將它們出棧素征。
使用遞歸可以很好地實(shí)現(xiàn)深度優(yōu)先搜索集嵌。只能說遞歸是深度優(yōu)先搜索的一種實(shí)現(xiàn)方式。在使用遞歸時(shí)御毅,系統(tǒng)會調(diào)用一個(gè)叫系統(tǒng)棧的東西來存放遞歸中每一層的狀態(tài)根欧,因此使用遞歸來實(shí)現(xiàn)DFS的本質(zhì)其實(shí)還是棧。
注意端蛆,當(dāng)DFS復(fù)雜度過高凤粗,或數(shù)據(jù)極端的情況,必要的時(shí)候需用到剪枝今豆。
關(guān)于dfs參數(shù)問題嫌拣,什么在變化,就把什么設(shè)置成參數(shù)呆躲。

DFS模板
void dfs()  {  //參數(shù)用來表示狀態(tài)
    if(到達(dá)終點(diǎn)狀態(tài))  {  
        ...//根據(jù)題意添加  
        return;  
    }  
    if(越界或者是不合法狀態(tài))  
        return;  
    if(特殊狀態(tài))//剪枝
        return ;
    for(擴(kuò)展方式)  {  
        if(擴(kuò)展方式所達(dá)到狀態(tài)合法)  {  
            修改操作;//根據(jù)題意來添加  
            標(biāo)記异逐;  
            dfs();  
            (還原標(biāo)記)插掂;  
            //是否還原標(biāo)記根據(jù)題意  
            //如果加上(還原標(biāo)記)就是 回溯法  
        }  
    }  
}  

DFS 幾道題目 推薦做一下

實(shí)例1(全排列加素?cái)?shù)):

已知 n 個(gè)整數(shù)b1,b2,…,bn
以及一個(gè)整數(shù) k(k<n)灰瞻。
從 n 個(gè)整數(shù)中任選 k 個(gè)整數(shù)相加,可分別得到一系列的和辅甥。
例如當(dāng) n=4酝润,k=3,4 個(gè)整數(shù)分別為 3璃弄,7要销,12,19 時(shí)夏块,可得全部的組合與它們的和為:
    3+7+12=22  3+7+19=29  7+12+19=38  3+12+19=34疏咐。
現(xiàn)在,要求你計(jì)算出和為素?cái)?shù)共有多少種,并給出每種組合的和脐供。
例如上例凳鬓,只有一種的和為素?cái)?shù):3+7+19=29。

輸入

第一行兩個(gè)整數(shù):n , k (1<=n<=20患民,k<n)
第二行n個(gè)整數(shù):x1,x2缩举,…,xn (1<=xi<=5000000)

輸出

一個(gè)整數(shù)(滿足條件的方案數(shù))。

樣例輸入
4 3
3 7 12 19
樣例輸出
1
29
#include<bits/stdc++.h>
using namespace std;

int sum = 0;
int n, k;
int a[105];
int p[105];
bool vis[105];
vector<int> ans; 

bool isprime(int sum) {
    if(sum <= 1) return false;
    for(int i = 2; i * i <= sum ; i++) {
        if(sum % i == 0) {
            return false;
        }
    }
    return true;
}

void dfs(int index) {
    if(index == k + 1 ) {
        if(isprime(sum)) {
            ans.push_back(sum);
        }
        return; 
    }
    for(int i = 1; i <= n; i++) {
        if(vis[i] == false && i > p[index - 1]) { //按序排列 剪枝 
            p[index] = i;
            vis[i] = true;
            sum += a[i];
            
            dfs(index + 1);
            
            vis[i] = false;
            sum -= a[i];         
        }
    }
}

int main() {
    memset(vis, 0, sizeof(vis));
    cin>> n >> k;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        p[i] = i;
    }
    
    dfs(1);
    
    cout << ans.size() << endl;
    for(vector<int>::iterator it = ans.begin();it != ans.end(); it++) {
        printf("%d\n", *it);
    }
    return 0;
}

廣度優(yōu)先搜索(BFS)

Breadth First Search , BFS, 以廣度為第一關(guān)鍵詞仅孩。
當(dāng)碰到岔道口時(shí)托猩,總是先依次訪問從該岔道口能直接到達(dá)的所有結(jié)點(diǎn),然后再按這些結(jié)點(diǎn)被訪問的順序去依次訪問它們能直接到達(dá)的所有結(jié)點(diǎn)辽慕,依次類推京腥,直到所有結(jié)點(diǎn)都被訪問為止。

BFS模板
void BFS(int s) {
    queue<int> q;
    q.push(s);
    while(!q.empty()) {
        node top = q.front();
        //取出隊(duì)首元素top;    
        ...
        //訪問隊(duì)首元素top操作;
        q.pop();BFD
        //將隊(duì)首元素出隊(duì);
        將top的下一層結(jié)點(diǎn)中未曾入隊(duì)的結(jié)點(diǎn)全部入隊(duì)溅蛉,并設(shè)置為已入隊(duì); 
    }
} 
實(shí)現(xiàn)步驟:
  1. 定義隊(duì)列q公浪,并將起點(diǎn)s入隊(duì)
  2. 寫一個(gè)while循環(huán),循環(huán)條件是隊(duì)列是q非空
  3. 在while循環(huán)中船侧,先取出隊(duì)首元素top欠气,然后訪問它(訪問可以是任何事情,例如將其輸出)镜撩。訪問完將其出隊(duì)预柒。
  4. 將top的下一層結(jié)點(diǎn)中所有未曾入隊(duì)的結(jié)點(diǎn)入隊(duì),并標(biāo)記它們的層號為now的層號加1袁梗,同時(shí)設(shè)置這些入隊(duì)的結(jié)點(diǎn)已入過隊(duì)宜鸯。
  5. 返回步驟2繼續(xù)循環(huán)
實(shí)例2:

給出一個(gè)m×n的矩陣,矩陣中的元素為0或1遮怜。稱位置(x, y)與其上下左右四個(gè)位置(x, y+1)淋袖、(x, y-1)、(x+1,y)锯梁、(x-1,y)是相鄰的即碗。如果矩陣中有若干個(gè)1是相鄰的(不必兩兩相鄰),那么稱這些1構(gòu)成了一個(gè)“塊"涝桅。求給定的矩陣中”塊“的個(gè)數(shù)

樣例輸入
6 7
0 1 1 1 0 0 1
0 0 1 0 0 0 0
0 0 0 0 1 0 0
0 0 0 1 1 1 0
1 1 1 0 1 0 0
1 1 1 1 0 0 0
樣例輸出
4
代碼
#include<cstdio>
#include<queue>
using namespace std;
const int maxn = 100;
struct node {
    int x, y; //位置(x, y) 
} Node;

int n, m;   //矩陣大小為n*m
int matrix[maxn][maxn];     //01矩陣
bool inq[maxn][maxn] = {false};     //記錄位置(x, y)是否已入過隊(duì)
int X[4] = {0, 0, 1, -1};   //增量數(shù)組 
int Y[4] = {1, -1, 0, 0}; 

bool judge(int x, int y) {      //判斷坐標(biāo)(x, y)是否需要訪問 
    //越界返回false
    if(x >= n || x < 0 || y >= m || y < 0) return false; 
    //不滿足條件返回false 
    if(matrix[x][y] == 0 || inq[x][y] == true) return false;
    return true; 
}

//BFS函數(shù)訪問位置(x, y)所在的塊拜姿,將該塊中所有"1"的inq都設(shè)置為true
void BFS(int x, int y) {
    queue<node> Q;      //定義隊(duì)列
    Node.x = x, Node.y = y;     //當(dāng)前結(jié)點(diǎn)坐標(biāo)為(x, y)
    Q.push(Node);       //將結(jié)點(diǎn)Node入隊(duì)
    inq[x][y] = true;       //設(shè)置(x, y)已入過隊(duì)
    while(!Q.empty()) {
        node top = Q.front();   //取出隊(duì)首元素
        Q.pop();        //隊(duì)首元素出隊(duì) 
        for(int i = 0; i < 4; i++) {    //循環(huán)四次烙样,得到四個(gè)相鄰位置 
            int newX = top.x + X[i];
            int newY = top.y + Y[i];
            if(judge(newX, newY)) {     //如果新位置(newX, newY)需要訪問 
                //設(shè)置Node的坐標(biāo)為(newX, newY)
                Node.x = newX, Node.y = newY;
                Q.push(Node);       //將結(jié)點(diǎn)Node加入隊(duì)列
                inq[newX][newY] = true;     //設(shè)置位置(newX, newY)已入過隊(duì) 
            }
        }
    }
} 

int main() {
    scanf("%d%d", &n, &m);
    for(int x = 0; x < n; x++) {
        for(int y = 0; y < m; y++) {
            scanf("%d", &matrix[x][y]);
        }
    }   
    int ans = 0;            //存放塊數(shù)
    for(int x = 0; x < n; x++) {        //枚舉每一個(gè)位置 
        for(int y = 0; y < m; y++) {
            //如果元素為1冯遂, 且未入過隊(duì)
            if(matrix[x][y] == 1 && inq[x][y] == false) {
                ans++;      //塊數(shù)+1
                BFS(x, y);      //訪問整個(gè)塊,將該塊所有"1"的inq都標(biāo)記未true 
            } 
        }
    } 
    printf("%d\n", ans);
    return 0;
} 
實(shí)例3:

給定一個(gè)nm大小的迷宮谒获,其中代表不可通過的墻壁蛤肌,而”.”代表平地,S表示起點(diǎn)批狱,T代表終點(diǎn)裸准。移動(dòng)過程中,如果當(dāng)前位置是(x, y)(下標(biāo)從0開始)赔硫,且每次只能前往上下左右(x, y+1)炒俱、(x, y-1)、(x+1, y)、(x-1, y)四個(gè)位置的平地权悟,求從起點(diǎn)S到達(dá)終點(diǎn)T的最少步數(shù)砸王。

樣例輸入
5 5
.....
.*.*.
.*S*.
.***.
...T*
2 2 4 3
樣例輸出
11
代碼
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 100;
struct node {
    int x, y;   //位置(x, y)
    int step;   //step為從起點(diǎn)S到達(dá)該位置的最少步數(shù)(即層數(shù)) 
}S, T, Node;    //S為起點(diǎn),T為終點(diǎn)峦阁,Node為臨時(shí)終點(diǎn)

int n, m;   //n為行谦铃,m為列
char maze[maxn][maxn];  //迷宮信息
bool inq[maxn][maxn] = {false};     //記錄位置(x, y)是否已入過隊(duì)
int X[4] = {0, 0, 1, -1};   //增量數(shù)組
int Y[4] = {1, -1, 0, 0};   

//檢測位置(x, y)是否有效 
bool test(int x, int y) {
    if(x >= n || x < 0 || y >= m || y < 0) return false;    //超過邊界
    if(maze[x][y] == '*') return false; //墻壁*
    if(inq[x][y] == true) return false; //已入過隊(duì)
    return true;    //有效位置 
} 

int BFS() {
    queue<node> q;  //定義隊(duì)列
    q.push(S);  //將起點(diǎn)S入隊(duì)
    while(!q.empty()) {
        node top = q.front();   //取出隊(duì)首元素
        q.pop();    //隊(duì)首元素出隊(duì)
        if(top.x == T.x && top.y ==T.y) {
            return top.step;
        } 
        for(int i = 0; i < 4; i++) {    //循環(huán)4次,得到4個(gè)相鄰位置
            int newX = top.x + X[i];
            int newY = top.y + Y[i];
            if(test(newX, newY)) {
                //設(shè)置Node的坐標(biāo)為(newX, newY) 
                Node.x = newX;
                Node.y = newY;
                Node.step = top.step + 1;   //Node層數(shù)為top的層數(shù)加1
                q.push(Node);       //將結(jié)點(diǎn)Node加入隊(duì)列 
                inq[newX][newY] = true;     //設(shè)置位置(newX, newY)已入過隊(duì) 
            } 
        }
    } 
    return -1;      //無法到達(dá)終點(diǎn)T時(shí)返回-1 
} 

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++) {
        getchar();  //過濾掉每行后面的換行符
        for(int j = 0; j < m; j++) {
            maze[i][j] = getchar();
        } 
        maze[i][m + 1] = '\0';
    }
    scanf("%d%d%d%d", &S.x, &S.y, &T.x, &T.y);  //起點(diǎn)和終點(diǎn)的坐標(biāo)
    S.step = 0; //初始化起點(diǎn)的層數(shù)為0榔昔, 即S到S的最少步數(shù)為0 
    printf("%d\n", BFS());
    return 0;
} 
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驹闰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子撒会,更是在濱河造成了極大的恐慌嘹朗,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茧彤,死亡現(xiàn)場離奇詭異骡显,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)曾掂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進(jìn)店門惫谤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人珠洗,你說我怎么就攤上這事溜歪。” “怎么了许蓖?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵蝴猪,是天一觀的道長。 經(jīng)常有香客問我膊爪,道長自阱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任米酬,我火速辦了婚禮沛豌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赃额。我一直安慰自己加派,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布跳芳。 她就那樣靜靜地躺著芍锦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪飞盆。 梳的紋絲不亂的頭發(fā)上娄琉,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天次乓,我揣著相機(jī)與錄音,去河邊找鬼孽水。 笑死檬输,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的匈棘。 我是一名探鬼主播丧慈,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼主卫!你這毒婦竟也來了逃默?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤簇搅,失蹤者是張志新(化名)和其女友劉穎完域,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘩将,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吟税,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姿现。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肠仪。...
    茶點(diǎn)故事閱讀 38,687評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖备典,靈堂內(nèi)的尸體忽然破棺而出异旧,到底是詐尸還是另有隱情,我是刑警寧澤提佣,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布吮蛹,位于F島的核電站,受9級特大地震影響拌屏,放射性物質(zhì)發(fā)生泄漏潮针。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一倚喂、第九天 我趴在偏房一處隱蔽的房頂上張望每篷。 院中可真熱鬧,春花似錦务唐、人聲如沸雳攘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至刚照,卻和暖如春刑巧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工啊楚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吠冤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓恭理,卻偏偏與公主長得像拯辙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子颜价,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 1)這本書為什么值得看: Python語言描述涯保,如果學(xué)的Python用這本書學(xué)數(shù)據(jù)結(jié)構(gòu)更合適 2016年出版,內(nèi)容...
    孫懷闊閱讀 12,458評論 0 15
  • 一些概念 數(shù)據(jù)結(jié)構(gòu)就是研究數(shù)據(jù)的邏輯結(jié)構(gòu)和物理結(jié)構(gòu)以及它們之間相互關(guān)系周伦,并對這種結(jié)構(gòu)定義相應(yīng)的運(yùn)算夕春,而且確保經(jīng)過這...
    Winterfell_Z閱讀 5,705評論 0 13
  • DFS 以迷宮搜索為例說明DFS。 如圖8-1专挪,從起點(diǎn)開始前進(jìn)及志,當(dāng)碰到岔道口時(shí),總是選擇其中一條岔道前進(jìn)(例如圖中...
    一斗閱讀 581評論 0 0
  • 數(shù)據(jù)結(jié)構(gòu)和算法--二叉樹的實(shí)現(xiàn) 幾種二叉樹 1寨腔、二叉樹 和普通的樹相比速侈,二叉樹有如下特點(diǎn): 每個(gè)結(jié)點(diǎn)最多只有兩棵子...
    sunhaiyu閱讀 6,433評論 0 14
  • 第五話 殺人游戲 時(shí)間,以它難以察覺的姿態(tài)悄悄地進(jìn)行著∑嚷現(xiàn)在锌畸,已經(jīng)是秋季了。街道上飄揚(yáng)著剛剛枯萎的落葉靖避,那淡淡的風(fēng)...
    鄒航閱讀 232評論 0 1