Leetcode刷题之路-深度优先和广度优先相关
深度优先搜索(DFS)
从起点出发,在没有走过的点随意挑一个节点继续向下搜索,直至达成条件或无路可走就返回
template<typename T>
T dfs(根据具体情况选择传入的参数){
if(满足某个条件或者到达最深点){
return (T)value;
}
dfs(); //递归,往下一层搜索
}
广度优先搜索(BFS)
系统地展开并检查搜索对象的所有节点,它并不考虑结果可能的位置,彻底的搜索整个目标,知道找到结果为止。
template<typename T>
T bfs(){
queue<T> queue_node; //维护一个队列
while(!queue_node.empty()){
queue_node.pop(); //保证队列是空队列(一般新创建的队列可以跳过这步了)
}
queue_node.push(start); //给定搜索起点的初值
while(!queue_node.empty()){//直到队列为空才跳出循环
temp = queue_node.pop(); //出队用这个节点寻找它相邻的其他节点
for(遍历temp相邻的所有节点){
if(这个节点符合某种条件并且从未被访问过){
queue_node.push(这个节点); //从新进队,从而保证队列不为空,直至没有节点进队为止,那就表示搜索完毕了
}
}
}
}
841.钥匙和房间(图)
有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,...,N-1,并且房间里可能有一些钥匙能使你进入下一个房间。
在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j] 由 [0,1,...,N-1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。
最初,除 0 号房间外的其余所有房间都被锁住。
你可以自由地在房间之间来回走动。
如果能进入每个房间返回 true,否则返回 false。
示例:
输入: [[1],[2],[3],[]]
输出: true
解释:
我们从 0 号房间开始,拿到钥匙 1。
之后我们去 1 号房间,拿到钥匙 2。
然后我们去 2 号房间,拿到钥匙 3。
最后我们去了 3 号房间。
由于我们能够进入每个房间,我们返回 true。
输入:[[1,3],[3,0,1],[2],[0]]
输出:false
解释:我们不能进入 2 号房间。
提示:
1. 1 <= rooms.length <= 1000
2. 0 <= rooms[i].length <= 1000
3. 所有房间中的钥匙数量总计不超过 3000。
- [ 利用unordered_set的count方法返回是否存在已经进入过的“房子” ]
- [ 基于上面的条件,深度搜索所有可以进去的容器 ]
- [ 最后比较unordered_set里的元素个数是否集合个数 ]
class Solution {
public:
unordered_set<int> visited;
vector<vector<int>> ro;
bool canVisitAllRooms(vector<vector<int>>& rooms) {
ro = rooms;
canVisitAllRoomsDFS(0);
return visited.size() == ro.size();
}
void canVisitAllRoomsDFS(int i) {
visited.insert(i);
for (int val : ro[i]) {
if (!visited.count(val)) {
canVisitAllRoomsDFS(val);
}
}
}
};
257.二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
输入:
1
/ \
2 3
\
5
输出: ["1->2->5", "1->3"]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
- [ 这题用深搜遍历二叉树的每一条路径不难,主要是怎么把val转换成要求的string类型输出 ]
- [ dfs函数设计传入一个路径字符串path,当遍历到叶子节点时把path放入容器里 ]
- [ 时间复杂度O(n*n),空间复杂度O(n*n) ]
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
if(root == nullptr) return res; //如果根节点都不存在,那直接返回空的res
dfs(res,root,""); //开始深度优先搜索
return res;
}
void dfs(vector<string>& res,TreeNode* root,string path){
path += to_string(root->val); //数字转字符串,加到路径的字符串中
if(root->left==nullptr&&root->right==nullptr){//如果左右子节点都不存在,就是到头了
res.push_back(path); //把这条路径的字符串push到目标字符串数组中
return; //返回
}
//分别判断左节点和右节点是否为空,不为空,递归,传入path+"->"继续完善路径字符串直至到头
if(root->left!=NULL){
dfs(res,root->left,path+"->");
}
if(root->right!=NULL){
dfs(res,root->right,path+"->");
}
}
- [ 用广度优先搜索的方法则需要创建两个队列,一个队列存放根节点,另一个存放路径字符串 ]
- [ 队列一开始的初始状态是存放根节点,以及路径字符串的根节点的数值 ]
- [ 在一个while循环里每次循环里从两个队列各拿一个数据出来,判断相对根节点是否存在子节点 ]
- [ 如果不存在子节点,就把此次从队列拿出来的字符串push_back到目标容器 ]
- [ 分别判断左右子节点存不存在,存在的就把此次的字符串path+"->",放回队列的尾部 ]
- [ 当队列为空时,搜索完毕,跳出while循环体,返回结果 ]
- [ 时间复杂度O(n*n),空间复杂度O(n*n) ]
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> paths;
if (root == nullptr) {
return paths;
}
queue<TreeNode*> node_queue;
queue<string> path_queue;
node_queue.push(root); //为存放节点信息的队列赋初值根节点
path_queue.push(to_string(root->val)); //为路径字符串赋初值
while (!node_queue.empty()) { //跳出循环的条件是队列为空
TreeNode* node = node_queue.front(); //申请局部变量存放队头节点信息
string path = path_queue.front(); //申请局部变量存放队头字符串信息
node_queue.pop(); //出队
path_queue.pop(); //出队
if (node->left == nullptr && node->right == nullptr) {//如果没有左右子节点了
paths.push_back(path);//把这次出队的string加到目标容器中
} else {
if (node->left != nullptr) {//如果左边子节点存在
node_queue.push(node->left);//把左边子节点的地址入队
path_queue.push(path + "->" + to_string(node->left->val));//字符串拼接好,入队
}
if (node->right != nullptr) {//如果右边子节点存在
node_queue.push(node->right);//把右边子节点的地址入队
path_queue.push(path + "->" + to_string(node->right->val));//字符串拼接好之后也入队
}
}
//然后开始下次循环,直至队列为空。
}
return paths;