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;
posted @ 2020-09-07 14:30  JoyooO  阅读(166)  评论(0编辑  收藏  举报