DFS(Depth First Search,深度优先搜索)与BFS(Breadth First Search,广度优先搜索)总结与思考

Depth First Search(dfs)

不管有多少条路,先一条道走到底,不断往下往后遍历,直到无路可走,再返回到上一个状态继续。常用递归实现,非递归常用存储待访问结点。使用的原因是栈是后进先出的,而dfs前次遍历到终点,下次迭代需要回到上一个状态。

bfs步骤:

  1. 不断递归,直到尽头。
  2. 回溯到上一个可以继续的状态。

比如,现在有一个图,结点为 A,B,C,D,E,FA,B,C,D,E,F。其中,AAB,C,DB,C,D连接,BBE,FE,F连接。CCG,HG,H连接。

A
B
C
D
E
F
G
H
  1. 访问结点AA,把AA压入栈(此时栈:AA)。
  2. 继续向下访问BBBB入栈(此时栈:B,AB,A
  3. 继续向下访问EEEE入栈(此时栈:E,B,AE,B,A
  4. EE无法继续访问,EE出栈(此时栈:B,AB,A),返回到上一个可以继续向后访问的点BB
  5. 继续向下访问FFFF入栈(此时栈:F,B,AF,B,A)。
  6. FF无法继续访问,FF出栈(此时栈:B,AB,A),返回到上一个可以继续向后访问的点BB
  7. BB的子结点已经全部被访问,无法继续向下访问,BB出栈(此时栈:AA),返回到上一个可以继续向后访问的点AA
  8. 继续向下访问CCCC入栈(此时栈:C,AC,A)。
  9. 不断重复。直到栈为空。

代码(递归)

int goal_x = 9, goal_y = 9;     //目标的坐标,暂时设置为右下角
int n = 10 , m = 10;               //地图的宽高,设置为10 * 10的表格
int graph[n][m];        //地图
int used[n][m];         //用来标记地图上那些点是走过的
int px[] = {-1, 0, 1, 0};   //通过px 和 py数组来实现左下右上的移动顺序
int py[] = {0, -1, 0, 1};
int flag = 0;           //是否能达到终点的标志
void DFS(int graph[][], int used[], int x, int y)
{
    // 如果与目标坐标相同,则成功
    if (graph[x][y] == graph[goal_x][goal_y]) {     
        printf("successful");
        flag = 1;
        return ;
    }
    // 遍历四个方向
    for (int i = 0; i != 4; ++i) {    
        //如果没有走过这个格子          
        int new_x = x + px[i], new_y = y + py[i];
        if (new_x >= 0 && new_x < n && new_y >= 0 
            && new_y < m && used[new_x][new_y] == 0 && !flag) {
            
            used[new_x][new_y] = 1;     //将该格子设为走过

            DFS(graph, used, new_x, new_y);      //递归下去

            used[new_x][new_y] = 0;//状态回溯,退回来,将格子设置为未走过
        }
    }
}

代码出处

代码(非递归)

Class Node
{
   Char data;
   Public Node(char c)
   {
      this.data=c;
   }
}
public void dfs()
{
	//DFS uses Stack data structure
	Stack s=new Stack();
	s.push(this.rootNode);
	rootNode.visited=true;
	printNode(rootNode);
	while(!s.isEmpty())
	{
		Node n=(Node)s.peek();
		Node child=getUnvisitedChildNode(n);
		if(child!=null)
		{
			child.visited=true;
			printNode(child);
			s.push(child);
		}
		else
		{
			s.pop();
		}
	}
	//Clear visited property of nodes
	clearNodes();
}

代码出处

Breadth First Search(bfs)

访问一个结点时,先把可能的选择记录下来,可以按照顺序选择访问其中一个,再把它可能的选择记录下来,再返回选择另外一个,不断重复。bfs通常用队列存储可能的选择,因为队列先进先出的属性。

比如,现在有一个图,结点为 A,B,C,D,E,FA,B,C,D,E,F。其中,AAB,C,DB,C,D连接,BBE,FE,F连接。CCG,HG,H连接。

A
B
C
D
E
F
G
H
  1. 首先访问结点AA,把可能的选择(B,C,DB,C,D)记录下来,B,C,DB,C,D入队,
  2. 访问(BB),BB被访问后出队(此时队列:C,DC,D),记录BB可能的选择E,FE,F,这里E,FE,F入队(此时队列:C,D,E,FC,D,E,F)。
  3. 访问CC(注:这里就相当于返回访问了,而不是继续沿着BB的路径访问),CC出队,记录CC可能的选择,G,HG,H入队(此时队列:D,E,F,G,HD,E,F,G,H)。
  4. 不断重复上面的步骤,也就是队列不断出队入队,直到队列为空。

代码

Class Node
{
   Char data;
   Public Node(char c)
   {
      this.data=c;
   }
}
public void bfs()
{
	//BFS uses Queue data structure
	Queue q=new LinkedList();
	q.add(this.rootNode);
	printNode(this.rootNode);
	rootNode.visited=true;
	while(!q.isEmpty())
	{
		Node n=(Node)q.remove();
		Node child=null;
		while((child=getUnvisitedChildNode(n))!=null)
		{
			child.visited=true;
			printNode(child);
			q.add(child);
		}
	}
	//Clear visited property of nodes
	clearNodes();
}

代码出处

比较

数据结构

dfs:递归思想,栈结构。

bfs:队列结构

时间复杂度

大致相同。

使用

dfs适合目标明确,使用递归实现思路明确,但如果图深度过大,很可能会出现爆栈,需要改写成非递归形式。

bfs适合大范围寻找,使用队列存储待选择状态,不用递归实现,不会出现爆栈。

能用bfs尽量使用bfs,防止爆栈。

例题:岛的个数

LeetCode 200. 岛屿数量

dfs实现(递归)

int find_island(int *arr, int m, int n) {
    int res = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (arr[i * n + j] == 1) {
                res++;
                infect(arr, i, j, m, n);
            }
        }
    }
    return res;
}
void infect(int *arr, int i, int j, int m, int n) {
    if (i < 0 || i >= m || j < 0 || j >= n || arr[i * n + j] != 1)
        return;
    arr[i * n + j] = 2;
    infect(arr, i - 1, j, m, n);
    infect(arr, i + 1, j, m, n);
    infect(arr, i, j - 1, m, n);
    infect(arr, i, j + 1, m, n);
}

bfs实现

int bfs(int *arr, int m, int n) {

    int direction_x[] = {-1, 1, 0, 0};
    int direction_y[] = {0, 0, -1, 1};
    int res = 0;

    bool visited[m*n];
    memset(visited, false, sizeof(visited));
    queue<int> q;
    int x, y;
    int xx, yy;

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (arr[i * n + j] == 1 && !visited[i * n + j]) {
                q.push(i);
                q.push(j);
                visited[i * n + j] = true;
                res++;
                while (!q.empty())
                {
                    x = q.front();
                    q.pop();
                    y = q.front();
                    q.pop();
                    for (int k = 0; k < 4; k++){
                        xx = x + direction_x[k];
                        yy = y + direction_y[k];

                        if (xx < 0 || xx >= m || yy < 0 || yy >= n)
                            continue;
                        if (arr[xx * n + yy] == 1 && !visited[xx * n + yy]) {
                            visited[xx * n + yy] = true;
                            q.push(xx);
                            q.push(yy);
                        }
                    }
                }
            }
        }
    }
    return res;
}

参考

搜索思想——DFS & BFS(基础基础篇)

Leetcode 200 Number of Islands 岛的个数

Introduction to Graph with Breadth First Search(BFS) and Depth First Search(DFS) Traversal Implemented in JAVA

发布了18 篇原创文章 · 获赞 8 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览