【问题标题】:Graph time complexities图时间复杂度
【发布时间】:2020-08-31 07:23:55
【问题描述】:

最近参加google的编码考试,有关于Graph数据结构的题, 问题之一是,他们给出了一个有 N 个节点和 M 个边的无向图 G,他给出 Q 个查询,在每个查询中,他给出 X Y W,我们必须检查是否存在从 X 到 Y 的每条边的路径最多必须包含权重

每当我使用矩阵表示时,都会超出内存限制,如果我使用邻接表表示,则会超出时间限制。Question Image

顺便说一句,前几天考试结束了。

这是我的第一个问题。如果我犯了任何错误,请在下面评论

【问题讨论】:

    标签: algorithm data-structures graph graph-algorithm


    【解决方案1】:

    这可以在O(n log n + q log q) 中解决,而您的 DFS 解决方案是 O(m*q),而 adj 矩阵解决方案是 O(n^2) 空间

    要快速解决这个问题,您需要了解 DSU(Disjiont Set Union)数据结构(也称为 Union Find)。支持O(log n)部分节点的高效联合,O(log n)中也可以判断部分节点是否连接

    1. 按权重对所有给定边进行升序排序
    2. 按权重、升序对所有给定查询进行排序(同时保存查询索引,因为需要按顺序输出)
    3. 现在一一处理查询,如果查询请求带有边 <= w 的路径,则将所有仍未添加的边添加到符合条件的图中(使用 DSU)
    4. 现在可以通过检查start end 查询的节点是否连接来回答查询(使用DSU)

    示例代码(C++):

    #include <bits/stdc++.h>
    using namespace std;
    
    int Find(int u, vector<int>&P)
    {
        return P[u] < 0 ? u : P[u] = Find(P[u],P);
    }
    
    void Union(int u, int v, vector<int>&P)
    {
        u=Find(u,P);
        v=Find(v,P);
        if(u==v)return;
        P[u]=v;
    }
    
    int main()
    {
        //input is quite large so we might need fast I/O
        ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 
    
        int t,n,m,q;
        cin>>t;
    
        while(t--)
        {
            cin>>n>>m>>q;
            vector<int>P(n+1,-1),answers(q);
            vector<array<int,3>>edges; //<storing edges as [w, u, v]
            vector<array<int,4>>queries; //<storing queries as [W, x, y, queryId]
    
            for(int i=0; i<m; i++)
            {
                int u,v,w;
                cin>>u>>v>>w;
                edges.push_back({w,u,v});
            }
    
            for(int i=0; i<q; i++)
            {
                int x,y,W;
                cin>>x>>y>>W;
                queries.push_back({W,x,y,i});
            }
    
            sort(edges.begin(),edges.end());
            sort(queries.begin(),queries.end());
    
            int edgeId = 0;
    
            for(auto&query : queries){
                while(edgeId < edges.size() && edges[edgeId][0] <= query[0]){
                    Union(edges[edgeId][1], edges[edgeId][2], P);
                    edgeId++;
                }
                answers[query[3]] = Find(query[1],P) == Find(query[2], P);
            }
    
            for(int i=0; i<q; i++)
                cout<<answers[i]<<(i+1==q?"\n":" ");
        }
    
    }
    
    

    【讨论】:

    • 这是一个很棒的解决方案。所以,我必须使用图的边缘列表表示并将它们存储在 Min Heap 中。对于每个查询,如果它们的父母(代表)不相同,我必须合并所有权重小于 W 的顶点,最后检查 X 和 Y 父母是否相等。
    • 因为它是 undriected 我可以使用 Union-Find 算法。如果给定的图是有向的呢?
    猜你喜欢
    • 1970-01-01
    • 2011-10-31
    • 2016-02-13
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-12
    相关资源
    最近更新 更多