代码随想录算法训练营第58天 | 拓扑排序精讲、dijkstra(朴素版)精讲
拓扑排序精讲
题目链接/文章讲解:https://www.programmercarl.com/kamacoder/0117.%E8%BD%AF%E4%BB%B6%E6%9E%84%E5%BB%BA.html
理论基础:
概括来说,给出一个 有向图,把这个有向图转成线性的排序 就叫拓扑排序。
拓扑排序的过程,其实就两步:
- 找到入度为0 的节点,加入结果集
- 将该节点从图中移除
循环以上两步,直到 所有节点都在图中被移除了。
结果集的顺序,就是我们想要的拓扑排序顺序 (结果集里顺序可能不唯一)
那么如果我们发现结果集元素个数 不等于 图中节点个数,我们就可以认定图中一定有 有向环!
这也是拓扑排序判断有向环的方法。
解题思路:
为了每次可以找到所有节点的入度信息,我们要在初始化的时候,就把每个节点的入度 和 每个节点的依赖关系做统计。
代码如下:
cin >> n >> m;
vector<int> inDegree(n, 0); // 记录每个文件的入度
vector<int> result; // 记录结果
unordered_map<int, vector<int>> umap; // 记录文件依赖关系while (m--) {// s->t,先有s才能有tcin >> s >> t;inDegree[t]++; // t的入度加一umap[s].push_back(t); // 记录s指向哪些文件
}
找入度为0 的节点,我们需要用一个队列放存放。
因为每次寻找入度为0的节点,不一定只有一个节点,可能很多节点入度都为0,所以要将这些入度为0的节点放到队列里,依次去处理。
代码如下:
queue<int> que;
for (int i = 0; i < n; i++) {// 入度为0的节点,可以作为开头,先加入队列if (inDegree[i] == 0) que.push(i);
}
开始从队列里遍历入度为0 的节点,将其放入结果集。
while (que.size()) {int cur = que.front(); // 当前选中的节点que.pop();result.push_back(cur);// 将该节点从图中移除 }
把这个入度为0的节点从图中移除,本质是要将 该节点作为出发点所连接的节点的 入度 减一 就可以了,这样好能根据入度找下一个节点,代码如下:
while (que.size()) {int cur = que.front(); // 当前选中的节点que.pop();result.push_back(cur);// 将该节点从图中移除 vector<int> files = umap[cur]; //获取cur指向的节点if (files.size()) { // 如果cur有指向的节点for (int i = 0; i < files.size(); i++) { // 遍历cur指向的节点inDegree[files[i]] --; // cur指向的节点入度都做减一操作// 如果指向的节点减一之后,入度为0,说明是我们要选取的下一个节点,放入队列。if(inDegree[files[i]] == 0) que.push(files[i]); }}}
dijkstra(朴素版)精讲
题目链接/文章讲解:https://www.programmercarl.com/kamacoder/0047.%E5%8F%82%E4%BC%9Adijkstra%E6%9C%B4%E7%B4%A0.html
dijkstra算法:在有权图(权值非负数)中求从起点到其他节点的最短路径算法。
需要注意两点:
- dijkstra 算法可以同时求 起点到所有节点的最短路径
- 权值不能为负数
dijkstra三部曲:
- 第一步,选源点到哪个节点近且该节点未被访问过
- 第二步,该最近节点被标记访问过
- 第三步,更新非访问节点到源点的距离(即更新minDist数组)
minDist数组 用来记录 每一个节点距离源点的最小距离。
总结
第58天,继续加油