DFS(深搜)和BFS(廣搜)

DFS(深搜)和BFS(廣搜)

DFS

深度優先搜索屬於圖算法的一種,英文縮寫爲DFS即Depth First Search.其過程簡要來說是對每一個可能的分支路徑深入到不能再深入爲止,而且每個節點只能訪問一次.

舉例說明:

img

上圖是無向圖,從A節點開始進行深度優先搜索(以下的訪問次序並不是唯一的,第二個點既可以是B也可以是C,D),則我們可能得到如下的一個訪問過程:

A->B->E(沒有路了!回溯到B,發現還是沒路,回溯到A)->C->F->H->G->D(沒有路,最終回溯到A,發現A也沒路了)本次搜索結束。

注意:每次到最後一個沒有路的節點時,標記已經走過,上圖例子中第一個沒有路的節點時E,然後回溯到B,發現B也沒有路了,標記走過,再回溯到A,以此類推。

舉例

題目描述
​ 在一個 w∗h的矩形廣場上,每一塊 1∗1的地面都鋪設了紅色或黑色的瓷磚。小明現在站在某一塊黑色的瓷磚上,他可以從此處出發,移動到上下左右四個相鄰的且是黑色的瓷磚上。現在,他想知道,通過重複上述移動所能經過的黑色瓷磚數。

輸入
​ 第一行兩個正整數 h,w。(2≤h,w≤50)
​ 接下來輸入一個二維字符矩陣,每個字符爲 “.”,"#","@",分別代表黑色瓷磚,紅色瓷磚,初始位置。

輸出
​ 輸出一個整數,表示可以到達的瓷磚數。

 樣例輸入
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
.........

樣例輸出
59

數據規模與約定
​ 時間限制:1 s
​ 內存限制:256 M
100% 的數據保證 2≤h,w≤50​

​ 思路:從一個起點找到可走路徑,標記已走過,遞歸調用
注意:需將此處的行列與座標紙上的x,y區分開

#include <iostream>
using namespace std;

int h,w,tx,ty;
char mmap[55][55] = {0};
int ans = 1;
int dir[4][2] = {
    0,1,
    1,0,
    0,-1,
    -1,0
};


void func(int tx,int ty){
    if(tx < 0 ||tx >= w || ty < 0 || ty >= h){ //越界條件
        return ;
    }
    for(int i = 0; i < 4; i++){ //四個方向
        int x = tx + dir[i][0];
        int y = ty + dir[i][1];
        if(mmap[x][y] == '.'){
            mmap[x][y] = 0;
            ans++;
            func(x,y);
        }
    }

}


int main(){
    cin >> h >>w;
    for(int i = 0; i < w; i++){
        for(int j = 0; j < h; j++){
            cin >> mmap[i][j];
            if(mmap[i][j] == '@'){
                tx = i;
                ty = j;
            }
        }
    }
    func(tx,ty);
    cout << ans << endl;
    return 0;
}

上述題目的解法一般用在求圖中黑色的數量,波數等問題。

題目描述
​ 給出 n 件物品,每個物品有一個體積 Vi,從中取出若干件物品能夠組成的不同的體積和有多少種可能。例如,n=3 , Vi={1,3,4},那麼輸出 6 種不同的體積和爲 1,3,4,5,7,8。

輸入
​ 第一行一個正整數 n。(n≤20)
​ 第二行 n 個整數,表示 Vi。(1≤Vi≤50)

輸出
​ 輸出一個整數,表示不同體積的組合數。

樣例輸入
3
1 3 4

樣例輸出
6

數據規模與約定
​ 時間限制:1 s
​ 內存限制:256 M
​ 100% 的數據保證 n≤20

#include <iostream>
using namespace std;
int n,num[25],ans = 0;
int check[1005] = {1};

void func(int s,int sum){//從s開始選數字,和爲sum
    if(check[sum] == 0){
        check[sum] = 1;
        ans++;
    }
    for(int i = s; i <= n; i++){
        sum += num[i];
        func(i + 1, sum);
        sum -= num[i]; //回溯
    }
}

int main(){
    cin >> n;
    for(int i = 0; i < n; i++){
        cin >> num[i];
    }
    func(0,0); 
    cout << ans;
}

DFS可以解決集合問題

題目描述
​ 有很多人在門口排隊,每個人將會被髮到一個有效的通行密碼作爲門票。一個有效的密碼由 L 個小寫字母組成,至少有一個元音 (a,e,i,o,u)和兩個輔音,並且是按字母表順序出現的,例如 abc 是有效的,而 cba 不是。
​ 現在給定一個期望長度 L 和 C 個小寫字母,輸出所有有效密碼。

輸入
​ 第一行兩個正整數 L,C。(3≤L≤15,C≤26)
​ 接下來一行輸入 C個小寫字母。

輸出
​ 按照字母表順序輸出所有密碼,一行一個,若密碼超過 2500025000 時,只輸出前 2500025000 個密碼。

樣例輸入
4 6
a t c i s w

樣例輸出
acis
acit
aciw
acst
acsw
actw
aist
aisw
aitw
astw
cist
cisw
citw
istw

數據規模與約定
​ 時間限制:1 s
內存限制:256 M
​ 100% 的數據保證 3≤L≤15,C≤26

#include <iostream>
#include <algorithm>
using namespace std;
int L,C;
char num[26];
char ans[20];
int cnt = 0;
int fu,yuan,num2;

int func(int s, int left){ //從s開始還剩left個
    if(left == 0){ 
        if(yuan >= 1 && fu >= 2){
            for(int i = 0; i < L; i++){
                cout << ans[i];
            }
        
            cout << endl;
            num2++;
            if(num2 == 25000){
                return -1;
            }
        }
    }
    for(int i = s; i < C; i++){
        ans[cnt++] = num[i];
        int f = 0;
        if(num[i] == 'a' || num[i] == 'e' || num[i] == 'i' || num[i] == 'o' || num[i] == 'u'){ 
            yuan++;
            f = 1;
        }else{
            fu++;
        }
        if(func(i+1,left-1) == -1){
            return -1;
        }
        if(f == 1){ //回溯
            yuan--;
        }else{
            fu--;
        }
        cnt--;
    }
    return 0;
}

int main(){
    cin >> L >> C;
    for(int i = 0; i < C; i++){
        cin >> num[i];
    }
    sort(num,num+C);
    func(0,L);
}

題目描述
​ 讀入一個用鄰接矩陣存儲的無向圖,輸出它的深度優先遍歷序列。(以 1爲起點,按照編號越小權值越大的規則)

輸入
​ 第一行一個正整數 NN,表示節點個數。(5≤N≤20)
​ 接下來輸入一個鄰接矩陣,a[i,j]=0表示 i,j之間不存在邊,=1 說明存在邊。

輸出
​ 格式參照樣例

樣例輸入
8
0 1 1 0 0 0 0 0
1 0 0 1 1 0 0 0
1 0 0 0 0 0 1 1
0 1 0 0 0 1 0 0
0 1 0 0 0 1 0 0
0 0 0 1 1 0 0 0
0 0 1 0 0 0 0 1
0 0 1 0 0 0 1 0

樣例輸出
1-2-4-6-5-3-7-8

#include <iostream>
using namespace std;
int n;
int num[25][25],check[25],flag;

void func(int s){
    if(flag == 1){
        cout << '-';
    }    
    flag = 1;
    cout << s;
    for(int i = 1; i <= n; i++){
        if(num[s][i] == 1 && check[i] == 0){
            check[i] = 1;
            func(i);
        }
    }
}

int main(){
    cin >> n;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n ; j++){
            cin >> num[i][j];
        }
    }
    for(int i = 1; i <= n; i++) {
        if(check[i] == 0){
            check[i] = 1;
            func(i);
        }
    }
    return 0;
}

BFS

廣度優先搜索(BFS) 廣度優先搜索在進一步遍歷圖中頂點之前,先訪問當前頂點的所有鄰接結點。 a .首先選擇一個頂點作爲起始結點,並將其染成灰色,其餘結點爲白色。 b. 將起始結點放入隊列中。 c. 從隊列首部選出一個頂點,並找出所有與之鄰接的結點,將找到的鄰接結點放入隊列尾部,將已訪問過結點塗成黑色,沒訪問過的結點是白色。如果頂點的顏色是灰色,表示已經發現並且放入了隊列,如果頂點的顏色是白色,表示還沒有發現 d. 按照同樣的方法處理隊列中的下一個結點。

模板

#include<iostream>
#include<queue>
using namespace std;
#define max_n 500
struct Node{
    int x,y,num;
};
char mmap[max_n + 5][max_n + 5];
queue<Node> q;
  int n,m;
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){  //從1開始防止越界
        for(int j = 1; j <= m; j++){
            cin >> mmap[i][j];
            if(mmap[i][j] == 's'){
                q.push({i,j,0});
            }   
        }
    }
    while(!q.empty()){
        Node temp = q.front();
        q.pop();
        for(int i = 0; i < 4; i++){
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            //判斷是否越界
            /*if(x<=0 || y<= 0|| x>n || y>m){
            	continue;
            }*/
            if(mmap[x][y] == 'g'){ //終點
                cout << temp.num + 1 << endl;
                return 0;
            }
            if(mmap[x][y] == '.'){ //可以通過
                mmap[x][y] = 0;
                q.push({x,y,temp.num+1});
            }
        }

    }
    cout << "No" << endl;
    return 0;
}

題目描述
​ 小明剛剛參加完期中考試,“這次又能得班級第一了”,他沾沾自喜,想起之前一直努力學習的自己,他決定去西城紅場看個電影放鬆一下。現在小明想從學校走到電影院,因爲市政大力修路和挖地鐵,有些道路不允許步行,請判斷小明能否走到電影院(只能朝上下左右四個方向行走),如果能到,則輸出最短步數,如果不能到,則輸出 No.

輸入
​ 第 1 行兩個數 n 和 m 表示地圖有 n 行 m 列 2≤n,m≤500 第 2 行至第 n+1 行爲地圖,其中 s 表示學校 g表示電影院​ . 爲步行可以通過的點 # 爲步行不可通過的點

輸出
​ 能走到電影院輸出最少步數​ 不能走到電影院輸出 No

樣例輸入
4 4
…s
…##

.g…

樣例輸出
5

數據規模與約定
​ 時間限制:1 s
內存限制:256 M
100% 的數據保證 2≤n,m≤500

#include <iostream>
#include <queue>
using namespace std;

char mmap[505][505];
int n,m,sx,sy;
int dir[4][2]={
    0,1,
    1,0,
    -1,0,
    0,-1
};
struct Node{
    int x,y,cnt;
};
queue<Node> que;
int main(){
    cin >> n >> m;
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < m; j++) {
            cin >> mmap[i][j];
            if(mmap[i][j] == 's'){
                Node node;
                node.x= i;
                node.y = j;
                node.cnt = 0;
                que.push(node);
            }
        }
    }
    while(!que.empty()){
        Node temp = que.front();
        que.pop();
        for(int i = 0; i < 4; i++){
            int tx = temp.x + dir[i][0];
            int ty = temp.y + dir[i][1];
            if(tx < 0 || ty < 0 || tx >= n || ty >= m){
                continue;
            }
            if(mmap[tx][ty] == 'g'){
                cout << temp.cnt + 1 << endl;
                return 0;
            }
            if(mmap[tx][ty] == '.'){
                mmap[tx][ty] = 0;
                que.push({tx,ty,temp.cnt + 1});
            }
        }

    }
    cout << "No" << endl;
    return 0;
}

帶有條件的廣搜

題目描述
​ 小明看完了電影,是時候回家了,可是這時他突然得知小米之家的小米9現貨開賣了,這款手機小明已經想了一個多月,知道這個消息後的他十分興奮,一定要在回家之前先去小米之家買手機(城市中有一個或多個小米之家),請計算小明從電影院到任意一個小米之家買手機後回家的最短距離(只能朝上下左右四個方向行走,除了障礙物外,其他地方都可以通過),數據保證可以買完手機後回家。

輸入
​ 第 1 行兩個數 n 和 m 表示地圖有 n 行 m 列 2≤n,m≤2000​ 第 2 行至第 n+1 行爲地圖 其中 S 表示電影院 T 表示家 P 表示小米之家​ . 爲可以通過的點 # 爲障礙物

輸出
​ 一個整數 表示小明從電影院去小米之家再回家的總距離

樣例輸入
5 5
.S…
###…
…T
.P##.
P…

樣例輸出
11

數據規模與約定
​ 時間限制:5 s
​ 內存限制:256 M
​ 100% 的數據保證 2≤n,m≤2000

#include <iostream>
#include <queue>
using namespace std;

struct node {
    int x, y, s, f; //s代表幾步,f代表有沒有手機
};

int n, m, check[2005][2005];//查重數組,1代表沒手機經過,2代表有手機經過,3沒有手機和有手機都經過
char mmap[2005][2005];
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};

int main() {
    queue<node> que;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mmap[i][j];
            if (mmap[i][j] == 'S') {
                que.push({i, j, 0, 0});
                check[i][j] = 1;
            }
        }
    }
       while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 0; i < 4; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            if (temp.f == 0 && check[x][y] & 1) continue;//沒有手機
            if (temp.f == 1 && check[x][y] & 2) continue;//有手機
            if (temp.f == 1 && mmap[x][y] == 'T') {//有手機到家
                cout << temp.s + 1 << endl;
                return 0;
            }
            if (temp.f==0&& mmap[x][y] == '.' || mmap[x][y] == 'S' || mmap[x][y] == 'T') {//沒手機可以經過的路徑
                que.push({x, y, temp.s + 1, temp.f});
                check[x][y] +=  1;
            }
            if (temp.f==1&& mmap[x][y] == '.' || mmap[x][y] == 'S' ) {//有手機可以經過的路徑
                que.push({x, y, temp.s + 1, temp.f});
                check[x][y] +=  2;
            }
            if (mmap[x][y] == 'P') {
                que.push({x, y, temp.s + 1, 1});
                check[x][y] = 3;
            }
                    }
    }
    return 0;
}

廣搜搜索樹
559. N叉樹的最大深度

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/
//廣搜
/*class Solution {
public:
    int maxDepth(Node* root) {
        if(!root) return 0;
      queue<Node *> q;
      q.push(root);
      int deep_max = 0;
      while(!q.empty()){
          deep_max++;
          for(int i = q.size(); i > 0; i--){
               Node * temp = q.front();
               q.pop();
              for(auto a : temp->children){
                  q.push(a);
              }
          }

      }
      return deep_max;   
    }
};*/
//深搜
class Solution {
public:
    int maxDepth(Node* root) {
        if(root == NULL) return 0;
         int deep_max =  0;
         for(auto i : root->children){
            int temp = maxDepth(i);
            deep_max = max(deep_max,temp);
        }
        return deep_max+1;
    }
};

Leetcode 劍指 Offer 32 - II. 從上到下打印二叉樹 II

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> v;
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(root == nullptr) return v;
        queue<TreeNode * > q;
        q.push(root);
        vector<int> vec;
        while(!q.empty()){
            for(int i = q.size(); i; i--){
                TreeNode * temp = q.front();
                vec.push_back(temp->val);
                q.pop();
                if(temp->left){
                    q.push(temp->left);
                }
                 if(temp->right){
                    q.push(temp->right);
                }
            }
            v.push_back(vec);
            vec.clear();
        }
        return v;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章