力扣刷题 -- BFS 与 DFS

BFS 与 DFS

BFS 刷题

二叉树的最小深度

111. 二叉树的最小深度

// 队列存储着当前结点以及当前结点的深度
class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        
        queue<pair<TreeNode*, int>> q;
        q.emplace(root, 1);

        while (!q.empty()) {
            TreeNode* node = q.front().first;
            int depth = q.front().second;
            q.pop();

            if (!node->left && !node->right) return depth;

            if (node->left) q.emplace(node->left, depth + 1);
            if (node->right) q.emplace(node->right, depth + 1);
        }
        return 0;
    }
};

// 变量 cc 记录当前层数
class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        
        queue<TreeNode*> q;
        q.push(root);

        int cc = 0;
        while (!q.empty()) {
            int size = q.size();
            cc += 1;
            for (int i=0; i<size; i++) {
                TreeNode* node = q.front(); q.pop();
                
                if (node->right == nullptr && node->left == nullptr) 
                    return cc;
                
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
        }
        return cc;
    }
};
二叉树的层序遍历

102. 二叉树的层序遍历

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        if (root == nullptr) return {};

        vector<vector<int>> res;
        queue<TreeNode*> q;
        q.push(root);

        while (!q.empty()) {
            int num = q.size();
            vector<int> temp;
            for (int i=0; i<num; i++) {
                TreeNode* node = q.front(); q.pop();
                temp.push_back(node->val);
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
            res.push_back(temp);
        }
        return res;
    }
};
单词接龙

127. 单词接龙

在这里插入图片描述
方法一:BFS

  • 初始化队列 que 和一个哈希表 visited 中,visited 是为了不再重复访问单词。
  • 对于刚开始的单词 beginWord = "hit",我们每次只替换一个位置的字符,然后在 wordList 里查找是否有一样的字符串,若完全没有相同的字符串,相当于 beginWord 不能转换到 endWord;比如将中间的字符 i 替换为 o :
    • 如果修改后的单词与 endWord 相等,则返回对应的最短转换序列;
    • 如果修改的单词与 endWord 不相等,且在 wordList 数组中找到了对应的单词,以及没有访问过这个单词,则将这个单词放进 que 里,下一循环就重复这个的过程;
    • 如果修改的单词与 endWord 不相等,且在 wordList 数组中不存在对应的单词,则 que 不放入。
  • 整体过程可以视为一个很大的树或者图,如上图所示。从 hit 节点开始,可以分裂出 26*3 个结点(分支),其中 hit 也在里面,但是只有 hot 是在 wordList 中且没有 visited 过,因此只选择这个单词;后续的流程一样,hot 也可以分裂出很多分支,但是能存到队列中就需要看 wordListvisited 这两个哈希表。

方式二:双向 BFS

  • 使用两个队列 startQueendQue,分别从 beginWordendWord 开始。
  • 每次循环队列的过程中,选择元素较少的队列开始搜索,以减少搜索空间;较大的队列,不仅作为对立面,也用作较小队列的临时存储。
  • 对队列的元素进行访问流程与第一个方式大部分是相同,但有一个点不同:判断最短转换序列的条件是,当两个队列的元素相交时,表示 beginWord 可以转换为 endWord;具体来说,当在当前队列中找到在另一个队列的集合 visitedEnd 中存在的单词时,直接返回当前步数 + 1。

在这里插入图片描述

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

        if (wordlist.find(endWord) == wordlist.end())
            return 0;
        
        int count = 1;
        unordered_set<string> visited;

        queue<string> que;
        que.emplace(beginWord);

        visited.insert(beginWord);
        int wordSize = beginWord.size();

        while (!que.empty()) {
            int n = que.size();
            // 层序遍历 
            for (int i=0; i<n; i++) {
                string front = que.front(); que.pop();
                if (front == endWord) return count;

                for (int j=0; j<wordSize; j++) {
                    string str = front; 

                    for (int k=0; k<26; k++) {
                    	// 替换单个字符
                        str[j] = 'a' + k;
                        if (visited.find(str) == visited.end() && wordlist.find(str) != wordlist.end()) {
                            que.emplace(str);
                            visited.insert(str);
                        }
                        
                    }
                }
            }
            count++;
        }

        return 0;
    }
};

// 双向 BFS
class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> wordlist(wordList.begin(), wordList.end());

        if (wordlist.find(endWord) == wordlist.end())
            return 0;
        
        int count = 1;

        queue<string> beginQue;
        queue<string> endQue;
        beginQue.emplace(beginWord);
        endQue.emplace(endWord);
        
        unordered_set<string> visited;
        unordered_set<string> visitedEnd;
        visited.insert(beginWord);
        visitedEnd.insert(endWord);

        int wordSize = beginWord.size();

        while (!beginQue.empty() && !endQue.empty()) {
            if (beginQue.size() > endQue.size()) {
                swap(beginQue, endQue);
                swap(visited, visitedEnd);
            }

            int n = beginQue.size();
            // 层序遍历 
            for (int i=0; i<n; i++) {
                string front = beginQue.front(); beginQue.pop();

                for (int j=0; j<wordSize; j++) {
                    string str = front;

                    for (int k=0; k<26; k++) {
                        str[j] = 'a' + k;
                        if (visited.find(str) == visited.end() && wordlist.find(str) != wordlist.end()) {
                        	if (visitedEnd.find(str) != visitedEnd.end()) 
                        		// 这个 str 存在 visitedEnd 中,相交了
                            	return count+1; 
                            beginQue.emplace(str);
                            visited.insert(str);
                        } 
                    }
                }
            }
            count++;
        }

        return 0;
    }
};
BFS in Topological Sort

拓扑排序是对一个有向无环图(Directed Acyclic Graph,DAG)进行排序的算法,目的是将图中的顶点排成一个线性序列。且该序列必须满足下面两个条件:
(1) 每个顶点出现且只出现一次。
(2) 若存在一条从顶点A到顶点B的路径,那么在序列中顶点A出现在顶点 B 的前面。
注: 有向无环图 (DAG) 才有拓扑排序,非 DAG 图没有拓扑排序一说。

课程表

207. 课程表
prerequisites[i] = [ai, bi],表示如果要学习课程 ai 则必须先学习课程 bi ;因此是从 bi 指向 ai。
在这里插入图片描述

(1) 根据 prerequisites 数组利用 哈希表 map 构建整个课程图,以及利用 数组 in 存储每个顶点的入度情况;比如 {1:[3, 4]} 表明为顶点 1 同时指向 3 和 4,in[3] = 2 相当于顶点 3 的入度为 2 ;
(2) 利用队列 que,将入度为 0 的点,放到队列中 【当顶点的入度为 0 ,相当于对应的课程已经学习完了,若仍有些课程的入度不为 0,说明图中存在环,无法被选,完成不了所有课】;
(3) 从队列中得到入度为 0 的顶点,假设将这个顶点去掉,则对应的被指向的顶点的入度应被减一;若是入度被减为 0 ,则将这个被指向的顶点放入队列中。

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        unordered_map<int, vector<int>> map;
        vector<int> in(numCourses, 0);

        for (int i=0; i<prerequisites.size(); i++) {
            map[prerequisites[i][1]].push_back({prerequisites[i][0]});
            in[prerequisites[i][0]]++;
        }

        queue<int> que;
        for (int i=0; i<in.size(); i++) {
            if (in[i] == 0) que.push(i);  
        }
        
        int count = 0;
        while (!que.empty()) {
            int node = que.front(); que.pop();
            count++;

            for (int i=0; i<map[node].size(); i++) {
                if (in[map[node][i]] > 0) {
                    in[map[node][i]]--;
                    if (in[map[node][i]] == 0) que.push(map[node][i]);
                }
            }
        }
		// 入度为 0 的数量是否等于 numCourses 
        return count == numCourses;
    }
};
课程表 II

210. 课程表 II

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        unordered_map<int, vector<int>> map;
        vector<int>in(numCourses, 0);

        for (int i=0; i<prerequisites.size(); i++) {
            map[prerequisites[i][1]].push_back(prerequisites[i][0]);
            in[prerequisites[i][0]]++;
        }

        queue<int> que;
        for (int i=0; i<numCourses; i++) {
            if (in[i] == 0) que.push(i);
        }

        vector<int> ans;
        while (!que.empty()) {
            int node = que.front(); que.pop();
            ans.push_back(node);

            for (int i=0; i<map[node].size(); i++) {
                if (in[map[node][i]] > 0) {
                    in[map[node][i]]--;
                    if (in[map[node][i]] == 0) que.push(map[node][i]);
                }
            }
        }
        if (ans.size() < numCourses) return {};
        return ans;
    }
};
  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于力扣刷题C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷题中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷题中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷题中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷题中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷题中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷题中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷题中,使用 string 可以方便地处理字符串相关的问题。 9. 注意边界条件:在力扣刷题中,边界条件往往是解决问题的关键。需要仔细分析题目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷题中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值