图论——leetcode

本文通过多个力扣(LeetCode)题目,深入探讨了图论在解决实际问题中的应用,包括拓扑排序、最短路径、并查集、深度优先搜索等算法。涉及题目包括最小高度树、找到最终的安全状态、接雨水II、最小体力消耗路径、水位上升的泳池中游泳、网络延迟时间和K站中转内最便宜的航班等,展示了图论在解决复杂问题中的强大能力。

851. 喧闹和富有 - 力扣(LeetCode)

复习一下链式前向星
注意题目要求的是拓扑序严格大于的数,这个时候按顺序统计的拓扑序并不是严格大于的关系,因此要在拓扑图中不断更新

class Solution {
   
   
    const static int N = 510 * 510;
public:
    int ne[N], idx, h[N], e[N], ind[N];
    void add(int a, int b)
    {
   
   
        e[idx] = b, ne[idx] = h[a], h[a] = idx ++; 
    }
    vector<int> loudAndRich(vector<vector<int>>& richer, vector<int>& quiet) {
   
   
        //更有钱且最安静的人
        //寻找拓扑序,然后找到更安静的人
        //b ---> a
        idx = 0;
        memset(h, -1, sizeof h);
        int n = quiet.size();
        for(int i = 0; i < richer.size(); i ++ )
        {
   
   
            int a = richer[i][0], b = richer[i][1];
            add(a, b);//a到b的一条边
            ind[b] ++;
        }
        vector<int> ans(n);
        queue<int> q;
        for(int i = 0; i < n; i ++ )
        {
   
   
            if(!ind[i]) q.push(i);
            ans[i] = i;//初始答案
        }
        while(q.size())
        {
   
   
            int u = q.front();
            q.pop();
            for(int i = h[u]; i != -1; i = ne[i])
            {
   
   
                int j = e[i];//u比j更有钱
                ind[j] --;
                if(quiet[ans[j]] > quiet[ans[u]])//u更加安静
                    ans[j] = ans[u];
                if(!ind[j]) q.push(j);
            }
        }
        return ans;
    }
};

搜索版本,对每个点搜索拓扑序比他大的点,更新最小quiet

class Solution {
   
   
public:
    vector<int> loudAndRich(vector<vector<int>> &richer, vector<int> &quiet) {
   
   
        int n = quiet.size();
        vector<vector<int>> g(n);
        for (auto &r : richer) {
   
   
            g[r[1]].emplace_back(r[0]);
        }
        vector<int> ans(n, -1);
        function<void(int)> dfs = [&](int x) {
   
   
            if (ans[x] != -1) {
   
   
                return;
            }
            ans[x] = x;
            for (int y : g[x]) {
   
   
                dfs(y);
                if (quiet[ans[y]] < quiet[ans[x]]) {
   
   
                    ans[x] = ans[y];
                }
            }
        };
        for (int i = 0; i < n; ++i) {
   
   
            dfs(i);
        }
        return ans;
    }
};

310. 最小高度树 - 力扣(LeetCode)

对于无向图,如何进行拓扑排序,可以设置入度最小为1,这时拓扑序是外层小于内层。

class Solution {
   
   
public:
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
   
   
        //最大值最小
        //删除叶子节点,最终会剩下一个点或者两个点,如果有第三个点,那么可以删除
        vector<int> g[n];
        vector<int> ind(n);
        for(int i = 0; i < n - 1; i ++ )
        {
   
   
            g[edges[i][0]].push_back(edges[i][1]);
            g[edges[i][1]].push_back(edges[i][0]);
            ind[edges[i][0]] ++;
            ind[edges[i][1]] ++;
        }
        queue<int> q;
        vector<bool> st(n);
        vector<int> dist(n);
        int cnt = 0;
        for(int i = 0; i < n; i ++ )
        {
   
   
            if(ind[i] == 1)
            {
   
   
                q.push(i); 
                dist[i] = 0; 
            }
        }
        while(n - (cnt ++) > 2)//最终会剩余两个或者一个,这时到达了最内层
        {
   
   
            int u = q.front();
            q.pop();
            st[u] = true; // 删除
            for(int i = 0; i < g[u].size(); i ++ )
            {
   
   
                int j = g[u][i];
                if(!st[j])//为了防止反复,经过的不会再经过
                {
   
   
                    dist[j] = max(dist[j], dist[u] + 1);
                    if(-- ind[j] == 1) q.push(j);
                }
            }
        }
        int res = *max_element(dist.begin(), dist.end());
        vector<int> ans;
        for(int i = 0; i < n; i ++ )
            if(dist[i] == res) ans.push_back(i);
        return ans;
    }
};

802. 找到最终的安全状态 - 力扣(LeetCode)

反向建图 + 拓扑排序
一个节点不会进入环则是安全的,正向无法保证这个点的入度为0,反向建图,从终点开始拓扑排序,找到所有满足要求的点

class Solution {
   
   
public:
    vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
   
   
        //找到拓扑图的所有点
        int n = graph.size();
        vector<int> ind(n);
        vector<int> g[n];
        for(int i = 0; i < n; i ++ )
        {
   
   
            for(int j = 0; j < graph[i].size(); j ++ )
            {
   
   
                ind[i] ++;
                g[graph[i][j]].push_back(i);//反向边
            }
        }
        vector<int> ans;
        queue<int> q;
        for(int i = 0; i < n; i ++ )
        {
   
   
            if(!ind[i])
            {
   
   
                q.push(i);
            }   
        }
        while(q.size())
        {
   
   
            auto u = q.front();
            q.pop();
            ans.push_back(u);
            for(int i = 0; i < g[u].size(); i ++ )
            {
   
   
                int j = g[u][i];
                ind[j] --;
                if(!ind[j]) q.push(j);
             }
        }
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值