有 n
个城市,按从 0
到 n-1
编号。给你一个边数组 edges
,其中 edges[i] = [fromi, toi, weighti]
代表 fromi
和 toi
两个城市之间的双向加权边,距离阈值是一个整数 distanceThreshold
。
返回能通过某些路径到达其他城市数目最少、且路径距离 最大 为 distanceThreshold
的城市。如果有多个这样的城市,则返回编号最大的城市。
注意,连接城市 i 和 j 的路径的距离等于沿该路径的所有边的权重之和。
示例 1:
![](https://assets.leetcode-cn.com/aliyun-lc-
upload/uploads/2020/01/26/find_the_city_01.png)
**输入:** n = 4, edges = [[0,1,3],[1,2,1],[1,3,4],[2,3,1]], distanceThreshold = 4
**输出:** 3
**解释:** 城市分布图如上。
每个城市阈值距离 distanceThreshold = 4 内的邻居城市分别是:
城市 0 -> [城市 1, 城市 2]
城市 1 -> [城市 0, 城市 2, 城市 3]
城市 2 -> [城市 0, 城市 1, 城市 3]
城市 3 -> [城市 1, 城市 2]
城市 0 和 3 在阈值距离 4 以内都有 2 个邻居城市,但是我们必须返回城市 3,因为它的编号最大。
示例 2:
![](https://assets.leetcode-cn.com/aliyun-lc-
upload/uploads/2020/01/26/find_the_city_02.png)
**输入:** n = 5, edges = [[0,1,2],[0,4,8],[1,2,3],[1,4,2],[2,3,1],[3,4,1]], distanceThreshold = 2
**输出:** 0
**解释:** 城市分布图如上。
每个城市阈值距离 distanceThreshold = 2 内的邻居城市分别是:
城市 0 -> [城市 1]
城市 1 -> [城市 0, 城市 4]
城市 2 -> [城市 3, 城市 4]
城市 3 -> [城市 2, 城市 4]
城市 4 -> [城市 1, 城市 2, 城市 3]
城市 0 在阈值距离 2 以内只有 1 个邻居城市。
提示:
2 <= n <= 100
1 <= edges.length <= n * (n - 1) / 2
edges[i].length == 3
0 <= fromi < toi < n
1 <= weighti, distanceThreshold <= 10^4
- 所有
(fromi, toi)
都是不同的。
解题思路
单源最短路径问题:Dijkstra、Bellman-Ford、SPFA
全源最短路径问题:Floyd
模板参考:《算法笔记》
所有算法代码附有本题执行结果图片,读者可以自行比对。
方法一:Floyd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| class Solution { public: void Floyd(vector<vector<int>>& distances, int n){ for (int k=0; k<n; ++k) { for (int i=0; i<n; ++i) { for (int j=0; j<n; ++j) { if (distances[i][k]!=INT_MAX && distances[k][j] != INT_MAX && distances[i][k] + distances[k][j] < distances[i][j]) { distances[i][j] = distances[i][k] + distances[k][j]; } } } } } int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) { vector<vector<int>>distances(n,vector<int>(n,INT_MAX)); for (int i=0; i<n; i++) { distances[i][i] = 0; } for (vector<int> edge : edges) { int u=edge[0], v=edge[1], w=edge[2]; distances[u][v]=w; distances[v][u]=w; } Floyd(distances, n); int idx = -1, minCount = INT_MAX; for (int i=0; i<n; ++i) { int count = 0; for (int j=0; j<n; ++j) { if (distances[i][j]<=distanceThreshold && i!=j) { count++; } } if (count <= minCount) { minCount = count; idx = i; } } return idx; } };
|
执行效率:
执行用时:84 ms, 在所有 C++ 提交中击败了64.89%的用户
内存消耗:12.2 MB, 在所有 C++ 提交中击败了28.24%的用户
使用C静态数组可极大提速,也是本题的最优解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Solution { public: int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) { const int INF = 0x3f3f3f3f; int dist[n][n]; memset(dist,INF,sizeof(dist)); for(int i=0;i<n;i++) dist[i][i]=0; for(int i=0;i<edges.size();i++){ dist[edges[i][0]][edges[i][1]]=edges[i][2]; dist[edges[i][1]][edges[i][0]]=edges[i][2]; } for(int k=0;k<n;k++){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]); } } } int id=-1, minCnt=INF; for(int i=0;i<n;i++){ int cnt=0; for(int j=0;j<n;j++){ if(dist[i][j]<=distanceThreshold) cnt++; } if(cnt<=minCnt){ minCnt=cnt; id=i; } } return id; } };
|
执行效率:
执行用时:20 ms, 在所有 C++ 提交中击败了99.61%的用户
内存消耗:11.3 MB, 在所有 C++ 提交中击败了93.39%的用户
方法二:Dijkstra
利用优先队列替换for循环是另一种写法,第二栏是priority_queue版,实测本题使用priority_queue运行速度反而下降。
邻接矩阵版:
[]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| class Solution { public: void Dijkstra(vector<vector<int>>& graph, vector<int>& distances, vector<bool>& visited, int n, int distanceThreshold, int start) { distances[start] = 0; for (int i=0; i<n; ++i) { int u=-1, minDis = INT_MAX; for (int j=0; j<n; ++j) { if (!visited[j] && distances[j] < minDis) { u = j; minDis = distances[j]; } } if (u==-1) return; visited[u] = true; for (int v=0; v<n; ++v) { if (!visited[v] && graph[u][v] != INT_MAX) { if (distances[u] + graph[u][v] < distances[v]) { distances[v] = distances[u] + graph[u][v]; } } } } } int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) { vector<vector<int>>graph(n,vector<int>(n,INT_MAX)); for (vector<int> edge : edges) { int u=edge[0], v=edge[1], w=edge[2]; graph[u][v] = graph[v][u] = w; } int idx = -1, minCount = INT_MAX; for (int i=0; i<n; ++i) { vector<int>distances(n,INT_MAX); vector<bool>visited(n,false); Dijkstra(graph, distances, visited, n, distanceThreshold, i); int count = 0; for (int j=0; j<n; ++j) { if (distances[j]<=distanceThreshold && i!=j) { count++; } } if (count <= minCount) { minCount = count; idx = i; } } return idx; } };
|
[]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class Solution { public: void Dijkstra(vector<vector<int>>& graph, vector<int>& distances, int n, int distanceThreshold, int start) { priority_queue <pair<int, int>,vector<pair<int, int>>, greater<pair<int, int>>> q; distances[start] = 0; q.push({distances[start],start}); while (!q.empty()) { pair<int, int>p = q.top(); int u = p.second; q.pop(); if (distances[u] < p.first) { continue; } for (int v=0; v<n; ++v) { if (graph[u][v] != INT_MAX && distances[v]>distances[u]+graph[u][v]) { distances[v]=distances[u]+graph[u][v]; q.push({distances[v],v}); } } } } int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) { vector<vector<int>>graph(n,vector<int>(n,INT_MAX)); for (vector<int> edge : edges) { int u=edge[0], v=edge[1], w=edge[2]; graph[u][v] = graph[v][u] = w; } int idx = -1, minCount = INT_MAX; for (int i=0; i<n; ++i) { vector<int>distances(n,INT_MAX); Dijkstra(graph, distances, n, distanceThreshold, i); int count = 0; for (int j=0; j<n; ++j) { if (distances[j]<=distanceThreshold && i!=j) { count++; } } if (count <= minCount) { minCount = count; idx = i; } } return idx; } };
|
执行效率:
执行用时:68 ms, 在所有 C++ 提交中击败了73.66%的用户
内存消耗:12.6 MB, 在所有 C++ 提交中击败了17.55%的用户
使用静态数组可极大提速
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| class Solution { public: int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) { const int INF = 0x3f3f3f3f; int graph[n][n]; memset(graph,INF,sizeof(graph)); int dist[n]; bool vis[n]; for (vector<int> edge : edges) { int u=edge[0], v=edge[1], w=edge[2]; graph[u][v] = graph[v][u] = w; } int idx = -1, minCnt = INF; for (int i=0; i<n; ++i) { memset(dist, INF, sizeof(dist)); dist[i] = 0; memset(vis, false, sizeof(vis)); for (int j=0; j<n; ++j) { int u=-1, minDis = INF; for (int k=0; k<n; ++k) { if (!vis[k] && dist[k] < minDis) { u = k; minDis = dist[k]; } } if (u==-1) { break; } vis[u] = true; for (int v=0; v<n; ++v) { if (!vis[v]) { dist[v] = min(dist[v], dist[u] + graph[u][v]); } } } int cnt = 0; for (int j=0; j<n; ++j) { if (dist[j] <= distanceThreshold && i != j) { cnt++; } } if (cnt <= minCnt) { minCnt = cnt; idx = i; } } return idx; } };
|
执行效率:
执行用时:28 ms, 在所有 C++ 提交中击败了97.67%的用户
内存消耗:11.8 MB, 在所有 C++ 提交中击败了47.47%的用户
邻接表版:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| class Solution { public: void Dijkstra(vector<vector<pair<int, int>>>&graph, vector<int>& distances, vector<bool>& visited, int n, int distanceThreshold, int start) { distances[start] = 0; for (int i=0; i<n; ++i) { int u=-1, minDis = INT_MAX; for (int j=0; j<n; ++j) { if (!visited[j] && distances[j] < minDis) { u = j; minDis = distances[j]; } } if (u==-1) return; visited[u] = true; for (int j=0; j<graph[u].size(); ++j) { int v = graph[u][j].first; int w = graph[u][j].second; if (!visited[v]) { if (distances[u] + w < distances[v]) { distances[v] = distances[u] + w; } } } } } int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) { vector<vector<pair<int, int>>>graph(n); for (vector<int> edge : edges) { int u=edge[0], v=edge[1], w=edge[2]; graph[u].push_back(make_pair(v, w)); graph[v].push_back(make_pair(u, w)); } int idx = -1, minCount = INT_MAX; for (int i=0; i<n; ++i) { vector<int>distances(n,INT_MAX); vector<bool>visited(n,false); Dijkstra(graph, distances, visited, n, distanceThreshold, i); int count = 0; for (int j=0; j<n; ++j) { if (distances[j]<=distanceThreshold && i!=j) { count++; } } if (count <= minCount) { minCount = count; idx = i; } } return idx; } };
|
执行效率:
执行用时:52 ms, 在所有 C++ 提交中击败了80.92%的用户
内存消耗:13.2 MB, 在所有 C++ 提交中击败了15.65%的用户
方法三:Bellman-Ford
注意:
1.这里用到边表,但是不能直接用题目给的vector<vector>& edges,必须要自己构建vector& edges,Edge是结构体,否则会超时。
2.题目给的是无向边,每条无向边实际上是两条有向边,分析时注意。
Bellman-Ford可根据距离判断图有没有负环,本题无需判断负环,模板中舍去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| class Solution { public: struct Edge{ int from; int to; int weight; }; void BellmanFord(vector<Edge>& edges, vector<int>& distances, int n, int start) { distances[start] = 0; for(auto& e:edges){ if(e.from == start) distances[e.to] = e.weight; } for(int i=0; i<n-1; ++i){ for(auto& e:edges){ if(distances[e.from] != INT_MAX && distances[e.to] > distances[e.from]+e.weight) distances[e.to] = distances[e.from]+e.weight; } } } int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) { vector<Edge> evec; for (vector<int> e: edges) { Edge e1,e2; e1.from = e[0]; e1.to = e[1]; e1.weight = e[2]; e2.from = e[1]; e2.to = e[0]; e2.weight = e[2]; evec.emplace_back(e1); evec.emplace_back(e2); } int idx = -1, minCount = INT_MAX; for (int i=0; i<n; ++i) { vector<int>distances(n,INT_MAX); BellmanFord(evec, distances, n, i); int count = 0; for (int j=0; j<n; ++j) { if (distances[j]<=distanceThreshold && i!=j) { count++; } } if (count <= minCount) { minCount = count; idx = i; } } return idx; } };
|
执行效率:
执行用时:956 ms, 在所有 C++ 提交中击败了5.38%的用户
内存消耗:13 MB, 在所有 C++ 提交中击败了16.16%的用户
方法四:SPFA
Bellman-Ford的优化,利用队列优化,队列升级为优先队列可进一步加速,有空更新SPFA的priority_queue加速版。
SPFA可根据顶点入队次数判断有没有负环,本题无需判断负环,模板中顶点入队次数数组舍去。
邻接表版(数组表示)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| class Solution { public: void SPFA(vector<vector<pair<int, int>>>& graph, vector<int>& distances, vector<bool>& inQueue, int start) { queue<int>q; q.push(start); distances[start] = 0; inQueue[start] = true; while (!q.empty()) { int u = q.front(); q.pop(); inQueue[u] = false; for (int k=0; k<graph[u].size(); ++k) { int v = graph[u][k].first; int w = graph[u][k].second; if (distances[u] + w < distances[v]) { distances[v] = distances[u] + w; if (!inQueue[v]) { q.push(v); inQueue[v] = true; } } } } } int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) { vector<vector<pair<int, int>>>graph(n); for (vector<int> edge : edges) { int u=edge[0], v=edge[1], w=edge[2]; graph[u].push_back(make_pair(v, w)); graph[v].push_back(make_pair(u, w)); } int idx = -1, minCount = INT_MAX; for (int i=0; i<n; ++i) { vector<int>distances(n,INT_MAX); vector<bool>inQueue(n,false); SPFA(graph, distances, inQueue, i); int count = 0; for (int j=0; j<n; ++j) { if (distances[j]<=distanceThreshold && i!=j) { count++; } } if (count <= minCount) { minCount = count; idx = i; } } return idx; } };
|
执行效率:
执行用时:72 ms, 在所有 C++ 提交中击败了61.68%的用户
内存消耗:14.3 MB, 在所有 C++ 提交中击败了16.60%的用户
邻接表版(静态链表表示)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| class Solution { static constexpr int MAXN = 1100, MAXM = MAXN*(MAXN-1)/2; public: struct Edge{ int from; int to; int weight; int next; }e[MAXM]; int head[MAXN]; int t; void add(int u, int v, int w) { e[t].from = u; e[t].to = v; e[t].weight = w; e[t].next = head[u]; head[u] = t++; } void SPFA(vector<int>& distances, vector<bool>& visited, int start) { queue<int>q; q.push(start); distances[start] = 0; while (!q.empty()) { int u = q.front(); q.pop(); visited[u] = false; for (int i=head[u]; i!=-1; i=e[i].next) { int v = e[i].to; if (distances[v]>distances[u]+e[i].weight) { distances[v] = distances[u]+e[i].weight; if (!visited[v]) { visited[v] = true; q.push(v); } } } } } int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) { t = 0; memset(head, -1, sizeof(head)); for (vector<int> e: edges) { add(e[0], e[1], e[2]); add(e[1], e[0], e[2]); } int idx = -1, minCount = INT_MAX; for (int i=0; i<n; ++i) { vector<int>distances(n,INT_MAX); vector<bool>visited(n,false); SPFA(distances, visited, i); int count = 0; for (int j=0; j<n; ++j) { if (distances[j]<=distanceThreshold && i!=j) { count++; } } if (count <= minCount) { minCount = count; idx = i; } } return idx; } };
|
执行效率:
执行用时:72 ms, 在所有 C++ 提交中击败了72.31%的用户
内存消耗:23.7 MB, 在所有 C++ 提交中击败了5.39%的用户
总结
执行效率最高的是Floyd和Dijkstra:
在使用vector的情况下,Dijkstra快于Floyd;
在使用C静态数组的情况下,Floyd快于Dijkstra;
C静态数组大大快于vector。
执行效率最低的是Bellman-Ford。
吐血模板大总结,有空将持续更新,欢迎收藏点赞评论。