1334. 阈值距离内邻居最少的城市 O(n^3)、O(n^2)【邻接矩阵(稠密图):任意两点间的最短路径】【Floyd、Dijkstra】

50 阅读3分钟

1334. 阈值距离内邻居最少的城市

Floyd是先固定中间节点,然后遍历所有其入度和出度的点。 Dijkstra是先固定源节点,然后遍历所有可能的中间节点和目的节点。

邻接矩阵(稠密图)还是邻接表(稀疏图)

Floyd

class Solution:
    def findTheCity(self, n: int, edges: List[List[int]], distanceThreshold: int) -> int:
       # Floyd 算法
       # 求 每个 结点 到 其它结点的最短路。 然后查看 满足 距离在  distanceThreshold 内的数
        res = (inf, -1) # 符合条件的邻居节点数  编号
        e = [[inf] * n for _ in range(n)] #  任意两点间的最短路径

        for u, v, w in edges:
           e[u][v] = w
           e[v][u] = w

        for node in range(n):  # O(n^3)
            e[node][node] = 0 
            for u in range(n):
                for v in range(n):
                    e[u][v] = min(e[u][v], e[u][node] + e[node][v])  # 邻接矩阵

        for u in range(n):
            cnt = sum(e[u][v] <= distanceThreshold for v in range(n))
            if cnt <= res[0]: # 相等时   返回的是 编号更大的
                res = (cnt, u)

        return res[1]

image.png

class Solution {
public:
    int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) {
        pair<int,int> res(INT_MAX / 2, -1);
        vector<vector<int>> e(n, vector<int>(n, INT_MAX / 2));
        for (auto eg : edges){// 
            int u = eg[0], v = eg[1], w = eg[2];
            e[u][v] = w;
            e[v][u] = w;
        }
        
        for (int k = 0; k < n; ++k){// 固定  中间结点
            e[k][k] = 0;
            for (int i = 0; i < n; ++i){// 入度节点
                for (int j = 0; j < n; ++j){// 出度节点
                    e[i][j] = min(e[i][j], e[i][k] + e[k][j]);  // 这里 相加了, 所以最大值只能是  INT_MAX / 2, 不然会溢出
                }
            }
        }

        for (int i = 0; i < n; ++i){
            int cnt = 0;
            for (int j = 0; j < n; ++j){
                if (e[i][j] <= distanceThreshold){
                    cnt++;
                }
            }
            if (cnt <= res.first){
                res = {cnt, i};
            }
        }
        return res.second;
    }
};

Dijkstra

class Solution:
    def findTheCity(self, n: int, edges: List[List[int]], distanceThreshold: int) -> int:
       # Dijkstra 算法
       # 求 每个 结点 到 其它结点的最短路。 然后查看 满足 距离在  distanceThreshold 内的数
        res = (inf, -1) # 最短路径  编号
        e = [[inf] * n for _ in range(n)]
        dis = [[inf] * n for _ in range(n)]  ## 
        visited = [[False] * n for _ in range(n)]

        for u, v, w in edges:
           e[u][v] = w
           e[v][u] = w

        ## 
        for i in range(n):                                    # 源节点 i 
            dis[i][i] = 0 
            for _ in range(n):  # 
                t = -1                                         # t 
                # 找 最短的。  # 总代价 最低的扩展  分支定界思想
                for k in range(n):  #  
                    if not visited[i][k] and (t == -1 or dis[i][k] < dis[i][t]): # t = -1 表示 刚进入该循环, 
                        t = k # 未 遍历的点中, t 离 i 最近。
                visited[i][t] = True  # i 和 t 之间 最短路径已确定
                for k in range(n): # 经过 i 和 t,      到达 k 的路径长度
                    dis[i][k] = min(dis[i][k], dis[i][t] + e[t][k])

        for u in range(n):
            cnt = sum(dis[u][v] <= distanceThreshold for v in range(n))  # 
            if cnt <= res[0]: # 相等时   返回的是 编号更大的
                res = (cnt, u)

        return res[1]

image.png

class Solution {
public:
    int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) {
        pair<int,int> res(INT_MAX / 2, -1);
        vector<vector<int>> e(n, vector<int>(n, INT_MAX / 2));
        vector<vector<int>> dis(n, vector<int>(n, INT_MAX / 2));
        vector<vector<int>> vis(n, vector<int>(n, false));

        for (auto eg : edges){// 
            int u = eg[0], v = eg[1], w = eg[2];
            e[u][v] = w;
            e[v][u] = w;
        }
        
        for (int i = 0; i < n; ++i){
            dis[i][i] = 0;  // 源节点
            for (int j = 0; j < n; ++j){// j 没 用。 只是 
                int t = -1;
                for (int k = 0; k < n; ++k){
                    if (!vis[i][k] && (t == -1 || dis[i][k] < dis[i][t])){
                        t = k;   // 未遍历节点 中 离 i 更近的结点 t  
                    }
                }
                vis[i][t] = true;  // 标记 已计算
                // 更新 别的结点
                for (int k = 0; k < n; ++k){// 经过 i 和 t 节点的路径  终点为 k 。 后续 有更短的,就更新
                    dis[i][k] = min(dis[i][k], dis[i][t] + e[t][k]);   // 注意 这里 加 的是  e[t][k]                 
                }
            }
        }

        for (int i = 0; i < n; ++i){
            int cnt = 0;
            for (int j = 0; j < n; ++j){
                if (dis[i][j] <= distanceThreshold){
                    cnt++;
                }
            }
            if (cnt <= res.first){
                res = {cnt, i};
            }
        }
        return res.second;
    }
};