欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

『算法』广度优先搜索

程序员文章站 2022-03-20 12:10:01
...

本篇博客我们来介绍一下广度优先搜索的基本思想。

什么是广度优先搜索


广度优先搜索(又称广度优先搜索)是最简便的图的搜索算法之一。英文缩写为BFS(Breadth First Search)。属于一种盲目搜索法,目的是系统地展开并检查图中的所有结点,以找寻结果。换句话说,它不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止

我们来看一个迷宫问题
假设有一个迷宫,里面有障碍物,迷宫用二维矩阵表示,标记为0的地方表示可以通过,标记为1的地方表示障碍物,不能通过。现在给一个迷宫出口,让你判断是否可以从入口进来之后,走出迷宫,每次可以向任意方向走。

  • 假设是一个10*10的迷宫,入口在(1, 1)的位置,出口在(8, 10)的位置,通过(1, 1)一步可以走到的位置有两个(1, 2)(2, 1)
  • 但是这两个点并不是出口,需要继续通过这两个位置进一步搜索,假设现在在(1, 2),下一次一步可以到达的新的位置为(1, 3)(2, 2)。而通过(2, 1)可以一步到达的新的位置为(2, 2)(3, 1),但是这里(2, 2)是重复的,所以没一个点在走的过程中需要标记是否已经走过了;
  • 两步之后,还没有走到出口,这时候需要通过新加入的点再去探索下一步能走到哪些新点上,重复这个过程,直到走到出口为止

我们直接来看实现代码

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

static int nextPos[4][2] = {
	{ 1, 0 }, { 0, 1 }, { -1, 0 }, {0, -1}
};

bool BFS(vector<vector<int>>& graph, int sx, int sy, int ex, int ey) {
	int row = graph.size();
	int col = graph[0].size();

	queue<pair<int, int>> q;
	vector<vector<int>> book(row, vector<int>(col, 0));

	q.push(make_pair(sx, sy));
	book[sx][sy] = 1;
	bool flag = false;
	while (!q.empty()) {
		for (int i = 0; i < 4; ++i) {
			int newX = q.front().first + nextPos[i][0];
			int newY = q.front().second + nextPos[i][1];

			if (newX >= row || newX < 0 || newY >= col || newY < 0) {
				continue;
			}

			if (graph[newX][newY] == 0 && book[newX][newY] == 0) {
				q.push(make_pair(newX, newY));
				book[newX][newY] = 1;
			}

			if (newX == ex && newY == ey) {
				flag = true;
				break;
			}
		}

		if (flag) break;

		q.pop();
	}

	return flag;
}

int main() {
	int sx, sy, ex, ey;
	int row, col;

	cout << "请输入迷宫的行和列: ";
	cin >> row >> col;

	vector<vector<int>> graph(row, vector<int>(col, 0));
	cout << "请输入迷宫: \n";
	for (int i = 0; i < row; ++i) {
		for (int j = 0; j < col; ++j) {
			cin >> graph[i][j];
		}
	}

	cout << "请输入迷宫的入口和出口: \n";
	cin >> sx >> sy >> ex >> ey;

	cout << "是否可以走出迷宫: " << BFS(graph, sx, sy, ex, ey) << endl;
	
	return 0;
}

『算法』广度优先搜索
根据上面的例子,我们可以提炼出广度优先搜索的模型

/*
 * BFS() {
 *     1. 建立起始步骤,队列初始化;
 *     2. 遍历队列中的每一种可能;
 *     while (队列不为空) {
 *         通过对头元素带出下一步的所有可能,并且一次入队;
 *         {
 *             判断当前情况是否达成目标:按照目标要求处理逻辑;
 *         }
 *         继续遍历队列中的剩余情况;
 *     }
 * }
 */

几个简单的例子加深理解


员工的重要性


/*
// Employee info
class Employee {
public:
    // It's the unique ID of each node.
    // unique id of this employee
    int id;
    // the importance value of this employee
    int importance;
    // the id of direct subordinates
    vector<int> subordinates;
};
*/
class Solution {
public:
    int getImportance(vector<Employee*> employees, int id) {
        if (employees.empty()) {
            return 0;
        }

        unordered_map<int, Employee*> info;
        for (const auto& e : employees) {
            info[e->id] = e;
        }

        int sum = 0;
        BFS(info, id, sum);

        return sum;
    }

    void BFS(unordered_map<int, Employee*>& info, int id, int& sum) {
        queue<int> q;
        q.push(id);

        while (!q.empty()) {
            sum += info[q.front()]->importance;

            for (const auto& subid : info[q.front()]->subordinates) {
                q.push(subid);
            }

			q.pop();
        }
    }
};

『算法』广度优先搜索

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:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> result;
        if (root == nullptr) {
            return result;
        }

        queue<Node*> q;
        q.push(root);
        while (!q.empty()) {
            int qSize = q.size();
            vector<int> curLevel(qSize);
            for (int i = 0; i < qSize; ++i) {
                Node* curNode = q.front();
                curLevel[i] = curNode->val;
                for (const auto& node : curNode->children) {
                    q.push(node);
                }

                q.pop();
            }

            result.push_back(curLevel);
        }

        return result;
    }
};

『算法』广度优先搜索

腐烂的橘子


class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        if (grid.empty()) {
            return 0;
        }

        int row = grid.size();
        int col = grid[0].size();

        queue<pair<int, int>> q;
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                if (grid[i][j] == 2) {
                    q.push(make_pair(i, j));
                }
            }
        }

        int nextPos[4][2] = {
            {1, 0}, {0, 1}, {-1, 0}, {0, -1}
        };

        int minutes = 0;
        while (!q.empty()) {
            ++minutes;

            int qSize = q.size();
            for (int i = 0; i < qSize; ++i) {
                pair curPair = q.front();
                for (int i = 0; i < 4; ++i) {
                    int newX = curPair.first + nextPos[i][0];
                    int newY = curPair.second + nextPos[i][1];

                    if (newX >= row || newX < 0 || newY >= col || newY < 0) {
                        continue;
                    }

                    if (grid[newX][newY] == 1) {
                        q.push(make_pair(newX, newY));
                        grid[newX][newY] = 2;
                    }
                }

                q.pop();
            }
        }

        bool flag = false;
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                if (grid[i][j] == 1) {
                    flag = true;
                    break;
                }
            }
        }

        minutes = minutes ? --minutes : minutes;
    
        return flag ? -1 : minutes;
    }
};

『算法』广度优先搜索

单词接龙


class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> wordSet(wordList.begin(), wordList.end());
        unordered_set<string> visited;

        int length = beginWord.size();
        int steps = 0;

        queue<string> q;
        q.push(beginWord);
        while (!q.empty()) {
            ++steps;

            int qSize = q.size();
            for (int i = 0; i < qSize; ++i) {
                string curWord = q.front();
                if (curWord == endWord) {
                    return steps;
                }

                for (int i = 0; i < length; ++i) {
                    string newWord(curWord);
                    for (char ch = 'a'; ch <= 'z'; ++ch) {
                        newWord[i] = ch;

                        if (!wordSet.count(newWord) || visited.count(newWord)) {
                            continue;
                        }

                        q.push(newWord);
                        visited.insert(newWord);
                    }
                }

                q.pop();
            }
        }

        return 0;
    }
};

『算法』广度优先搜索

打开转盘锁


class Solution {
public:
    int openLock(vector<string>& deadends, string target) {
        unordered_set<string> deadSet(deadends.begin(), deadends.end());
        if (deadSet.count("0000")) {
            return -1;
        }

        int steps = 0;
        unordered_set<string> visited;
        visited.insert("0000");

        queue<string> q;
        q.push("0000");
        while (!q.empty()) {
            int qSize = q.size();
            for (int i = 0; i < qSize; ++i) {
                string curPW = q.front();

                if (deadSet.count(curPW)) {
                    return -1;
                }

                if (curPW == target) {
                    return steps;
                }

                visited.insert(curPW);

                for (int j = 0; j < 4; ++j) {
                    string newPW1(curPW);
                    string newPW2(curPW);

                    newPW1[j] = newPW1[j] == '9' ? '0' : ++newPW1[j];
                    newPW2[j] = newPW2[j] == '0' ? '9' : --newPW2[j];

                    if (!deadSet.count(newPW1) && !visited.count(newPW1)) {
                        q.push(newPW1);
                        visited.insert(newPW1);
                    }
                    if (!deadSet.count(newPW2) && !visited.count(newPW2)) {
                        q.push(newPW2);
                        visited.insert(newPW2);
                    }
                }
                q.pop();
            }

            ++steps;
        }

        return -1;
    }
};

『算法』广度优先搜索