0 回顾
解决二叉树主要是有两种思想,分别是遍历思想以及分解思想,两种思想分别衍生出回溯算法与动态规划。二叉树除了递归遍历还有层序遍历,层序遍历即为BFS。
1 BFS
//计算从起点start到终点target的最近距离
int BFS(TreeNode* start, TreeNode* target)
{
queue<TreeNode*>q;//核心数据结构
//set<TreeNode*>visited;//避免走回头路
vector<TreeNode*>visited;
q.push(start);//起点加入队列
visited.push_back(start);
int step = 0;//记录扩散的步数
while (!q.empty())
{
int sz = q.size();//将当前队列中的所有节点向四周扩散
for (int i = 0; i < sz; ++i)
{
TreeNode* cur = q.front();
q.pop();
if (cur == target)//判断是否到达终点
return step;
//将cur的相邻节点加入队列
for (TreeNode* x : cur.adj())
{
if (!visited[x])
{
q.push(x);
visited.push_back(x);
}
}
}
//更新步数
++step;
}
}
可以看出,与二叉树的层序遍历是差不多的。
二叉树的层序遍历如下:
void levelTraverse(TreeNode* root)
{
if (root == nullptr) return;
queue<TreeNode*> q;
q.push(root);
//从上到下遍历二叉树的每一层
while (!q.empty())
{
int sz = q.size();
//从左到右遍历每一层的每个节点
for (int i = 0; i < sz; ++i)
{
TreeNode* cur = q.front();
q.pop();
//将下一层节点放入队列
if (cur->left != nullptr) q.push(cur->left);
if (cur->right != nullptr) q.push(cur->right);
}
}
}
综上,BFS就是层序遍历的一个推广,BFS一般都是找最值的,比如说找最小,最短路径之类的,当然,BFS与DFS对比起来,消耗的空间可能大一点。
2 刷题
2.1 二叉树的最小深度
2.1.1 题解
首先要知道起点和终点是什么,起点就是根节点了,终点是最靠近根节点的叶子节点。叶子结点的判断就是if (!cur->left && !cur->right)
。
2.1.2 Code
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == nullptr) return 0;
queue<TreeNode*>q;
q.push(root);//起点加入
int deepth = 1;//记录深度
while (!q.empty())
{
int size = q.size();
for (int i = 0; i < size; ++i)
{
TreeNode* cur = q.front();
q.pop();
if (cur->left == nullptr && cur->right == nullptr) return deepth;
if (cur->left != nullptr) q.push(cur->left);
if (cur->right != nullptr) q.push(cur->right);
}
deepth++;
}
return deepth;
}
};
2.1.3 结果
2.2 打开转盘锁
2.2.1 题解
注意看题,题目说,你需要给出解锁需要的最小旋转次数,这是一个求最值得问题,所以考虑使用BFS,但是怎么往BFS上靠呢?可以这样想,一共4个波轮,分为4位,每一位都可以往上/往下进行拨动,也就是最后有8种情况,那么这样,把最开始的0000想象成一个根节点,然后根节点分离出了8颗子树,也就是八叉树,然后就是分离出来的子树,也可以每一位往上/往下拨动波轮,这又是八叉树的延展,所以,就是一层一层的八叉树,这样,套用层序遍历也就是BFS的思想,就能搜索到我们需要的那个密码。
2.2.2 Code
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
if (target == "0000") {
return 0;
}
unordered_set<string> dead(deadends.begin(), deadends.end());
if (dead.count("0000")) {
return -1;
}
auto num_prev = [](char x) -> char {
return (x == '0' ? '9' : x - 1);
};
auto num_succ = [](char x) -> char {
return (x == '9' ? '0' : x + 1);
};
// 枚举 status 通过一次旋转得到的数字
auto get = [&](string& status) -> vector<string> {
vector<string> ret;
for (int i = 0; i < 4; ++i) {
char num = status[i];
status[i] = num_prev(num);
ret.push_back(status);
status[i] = num_succ(num);
ret.push_back(status);
status[i] = num;
}
return ret;
};
queue<pair<string, int>> q;
q.emplace("0000", 0);
unordered_set<string> seen = {"0000"};
while (!q.empty()) {
auto [status, step] = q.front();
q.pop();
for (auto&& next_status: get(status)) {
if (!seen.count(next_status) && !dead.count(next_status)) {
if (next_status == target) {
return step + 1;
}
q.emplace(next_status, step + 1);
seen.insert(move(next_status));
}
}
}
return -1;
}
};