算法训练1-day49-图论

22 阅读4分钟
  1. 117. 软件构建 拓扑排序,每次选择入度为0的节点,然后去掉这个节点,将它所指向的所有节点的入度都减一,然后将入度为0的新节点入队,重复上述操作直到队列为空;如果结果没有包含所有节点,那么就代表有环
#include <iostream>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

int main() {
  int n = 0, m = 0;
  cin >> n >> m;

  //   unordered_map<int, vector<int>> edges;
  vector<vector<int>> edges(n);
  vector<int> indegree(n, 0);
  int from, to;
  for (int i = 0; i < m; ++i) {
    cin >> from >> to;
    edges[from].push_back(to);
    indegree[to]++;
  }

  queue<int> q;
  // 获取所有入度为0的文件
  for (int i = 0; i < n; ++i) {
    if (indegree[i] == 0) {
      q.push(i);
    }
  }
  vector<int> result;
  while (!q.empty()) {
    int file = q.front();
    result.push_back(file);
    q.pop();
    for (int next : edges[file]) {
      indegree[next]--;
      if (indegree[next] == 0) {
        q.push(next);
      }
    }
  }

  if (result.size() == n) {
    for (int i = 0; i < n - 1; i++)
      cout << result[i] << " ";
    cout << result[n - 1];
  } else {
    cout << -1 << endl;
  }

  return 0;
}
  1. 47. 参加科学大会(第六期模拟笔试) Dijkstra算法,适用于有向图、边的权值没有负数,解决一个点到其他所有点的最短路径;采用贪心策略。维护一个集合S,包含已找到最短路径的节点。每次从集合S之外的节点中,选择一个距离源点最近的节点加入S,并松弛(更新)其所有邻居的距离。
    1. 准备distance数组,保存源点到i点的最短距离;准备visited数组,记录节点i是否从小根堆中弹出过
    2. 准备小根堆,保存节点i以及源点到节点i的距离(邻接表保存i和源点到i的距离,邻接矩阵保存x,y与源点到i的距离),小根堆根据距离组织
      1. 默认情况std::less → 大根堆 → 较大的元素在堆顶
      2. 比较函数返回true表示第一个参数应该排在第二个参数后面
    3. 设置distance[源点]为0,将(源点k, 0)加入小根堆
    4. 从小根堆中弹出节点(u, dis),然后:
      1. 如果visited[u]为true,continue。
      2. 如果visited[u]为false,设置visited[u]为true,然后检查u的所有边(k, weight):
        1. 检查是否越界(需要的话)
        2. 检查visited[k]== false且distance[u] + weight < distance[k],则更新distance[k]=distance[u] + weight,并将(k, distance[k])加入小根堆
        3. 处理u的所有边后,重复步骤4
    5. 小根堆为空,流程结束,distance表记录了源点到其他点的最短距离
#include <climits>
#include <iostream>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

int main() {
  int n = 0, m = 0;
  cin >> n >> m;
  vector<vector<int>> edges(n + 1, vector<int>(n + 1, INT_MAX));
  int from, to, weight;
  for (int j = 0; j < m; j++) {
    cin >> from >> to >> weight;
    edges[from][to] = weight;
  }

  int start = 1;
  int end = n;
  vector<int> distance(n + 1, INT_MAX);
  vector<bool> visited(n + 1);
  distance[start] = 0;
  for (int i = 1; i <= n; ++i) {

    int curr = 1;
    int minVal = INT_MAX;
    //找到离路径最近的节点
    for (int j = 1; j <= n; ++j) {
      if (!visited[j] && minVal > distance[j]) {
        curr = j;
        minVal = distance[j];
      }
    }

    //标记已访问
    visited[curr] = true;

    //更新从开始节点到其他节点的距离
    for (int j = 1; j <= n; ++j) {
      //节点还没有加入路径,且从开始节点到edge[0]的长度要大于从开始节点到curr再到edge[0]的长度,那么开始节点到edge[0]的长度进行更新
      if (!visited[j] && edges[curr][j] != INT_MAX &&
          distance[j] > distance[curr] + edges[curr][j]) {
        distance[j] = distance[curr] + edges[curr][j];
      }
    }
  }

  if (distance[end] == INT_MAX) {
    cout << -1 << endl;
  } else {
    cout << distance[end] << endl;
  }

  return 0;
}

// 使用最小堆来找下一次应该加入哪个节点
{
	//[743. 网络延迟时间](https://leetcode.cn/problems/network-delay-time/)
	struct CompareSecond
	{
	    //小根堆
	    bool operator()(pair<int, int>left, pair<int, int>right)
	    {
	        return left.second > right.second;
	    }
	};
	
	class Solution {
	public:
	    int dir[4][2] = { {0,1},{0,-1},{1,0},{-1,0} };
	    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
	        vector<vector<int>> network(n, vector<int>(n, INT_MAX));
	        //建立图
	        for (int i = 0; i < times.size(); ++i) {
	            network[times[i][0] - 1][times[i][1] - 1] = times[i][2];
	        }
			
			//源点到其他的最短距离
	        vector<int> distance(n, INT_MAX);
			//节点i是否从小根堆中弹出过
	        vector<int> visited(n, false);
	        //小根堆,保存节点i以及源点到节点i的距离
	        priority_queue 
	        < 
	        pair<int, int>, 
	        vector<pair<int, int>>, 
	        CompareSecond
	        > pq;
	        //初始化源点
	        distance[k-1] = 0;
	        pq.push(make_pair(k-1, 0));
	        while (!pq.empty())
	        {
	            auto info = pq.top();
	            pq.pop();
	            int u = info.first;
	            int dis = info.second;
	            //已经访问过,跳过
	            if (visited[u]) continue;
	
	            //标记访问过
	            visited[u] = true;
	            //遍历u的所有边
	            for (int i = 0; i < n; ++i) {
		            //是存在的边
	                if (network[u][i] != INT_MAX) {
		                //没有访问过,且从源点k到u再从u到i的距离要小于之前的从源点k到i的距离
	                    if (!visited[i] &&
	                        distance[u] + network[u][i] < distance[i]) {
	                        distance[i] = distance[u] + network[u][i];
	                        pq.push(make_pair(i, distance[i]));
	                    }
	                }
	            }
	        }
	
	        int ans = -1;
	        for (int i = 0; i < n; ++i) {
	            if (distance[i] == INT_MAX) return -1;
	            ans = max(ans, distance[i]);
	        }
	
	        return ans;
	    }
	};
}