leetcode刷题——数据结构(7):图

二分图

1. 判断是否为二分图

思考很久后可做出

拓扑排序

1. 课程安排的合法性

一开始未做出

2. 课程安排的顺序

思考后可做出,类上

 

并查集

1. 冗余连接

思考后可做出

 

 二分图

如果可以用两种颜色对图中的节点进行着色,并且保证相邻的节点颜色不同,那么这个图就是二分图。

1. 判断是否为二分图

785. Is Graph Bipartite? (Medium)

https://leetcode-cn.com/problems/is-graph-bipartite/

class Solution {
public:
    bool isBipartite(vector<vector<int>>& graph) {
        int n=graph.size(),flag[n];
        memset(flag,0,sizeof(flag));
        stack<int> st;
        for(int i=0;i<n;i++)
            if(flag[i]==0&&graph[i].size()>0){
                st.push(i);
                flag[i]=1;
                while(!st.empty()){
                    int cur=st.top();
                    st.pop();
                    for(int i=0;i<graph[cur].size();i++)
                        if(flag[graph[cur][i]]==0){
                            flag[graph[cur][i]]=-flag[cur];
                            st.push(graph[cur][i]);
                        }else if(flag[graph[cur][i]]!=-flag[cur]) 
                            return false;
                }
            }
        return true;
    }
};

拓扑排序

常用于在具有先序关系的任务规划中。

1. 课程安排的合法性

207. Course Schedule (Medium)

https://leetcode-cn.com/problems/course-schedule/

1. 入度表(广度优先遍历)

算法流程:

  1. 统计课程安排图中每个节点的入度,生成 入度表 indegrees。
  2. 通过课程前置条件列表 prerequisites 可以得到课程安排图的 邻接矩阵 adjacency,以下两种方法都会用到邻接矩阵
  3. 借助一个队列 queue,将所有入度为 0的节点入队。
    • 当 queue 非空时,依次将队首节点出队,在课程安排图中删除此节点 pre:
    • 并不是真正从邻接表中删除此节点 pre,而是将此节点对应所有邻接节点 cur 的入度 -1,即 indegrees[cur] -= 1。
  4. 当入度 -1后邻接节点 cur 的入度为 0,说明 cur 所有的前驱节点已经被 “删除”,此时将 cur 入队。
  5. 在每次 pre 出队时,执行 numCourses--;
    • 若整个课程安排图是有向无环图(即可以安排),则所有节点一定都入队并出队过,即完成拓扑排序。换个角度说,若课程安排图中存在环,一定有节点的入度始终不为 00。
    • 因此,拓扑排序出队次数等于课程个数,返回 numCourses == 0 判断课程是否可以成功安排。

复杂度分析:

时间复杂度 O(N + M),遍历一个图需要访问所有节点和所有临边,N和 M 分别为节点数量和临边数量;

空间复杂度 O(N),为建立邻接矩阵所需额外空间。

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> indegree(numCourses,0);
        vector<vector<int>> outpoints(numCourses);
        queue<int> q;
        for(auto p:prerequisites){
            indegree[p[0]]++;
            outpoints[p[1]].push_back(p[0]);
        }
        for(int i=0;i<numCourses;i++)
           if(indegree[i]==0) q.push(i);
        while(!q.empty()){
            int cur=q.front();
            q.pop();
            numCourses--;
            for(int op:outpoints[cur])
                if(--indegree[op]==0) q.push(op);
        }
        return numCourses==0;
    }
};

2.深度优先遍历(通过 DFS 判断图中是否有环)

算法流程:

  1. 借助一个标志列表 flags,用于判断每个节点 i (课程)的状态:
    • 未被 DFS 访问:i == 0;
    • 已被其他节点启动的DFS访问:i == -1;
    • 已被当前节点启动的DFS访问:i == 1。

2.对 numCourses 个节点依次执行 DFS,判断每个节点起步 DFS 是否存在环,若存在环直接返回 FalseFalse。DFS 流程;

终止条件:

  • 当 flag[i] == -1,说明当前访问节点已被其他节点启动的 DFS 访问,无需再重复搜索,直接返回 TrueTrue。
  • 当 flag[i] == 1,说明在本轮 DFS 搜索中节点 i 被第 2 次访问,即 课程安排图有环,直接返回 FalseFalse。

将当前访问节点 i 对应 flag[i] 置 1,即标记其被本轮 DFS 访问过;

递归访问当前节点 i 的所有邻接节点 j,当发现环直接返回 False;

当前节点所有邻接节点已被遍历,并没有发现环,则将当前节点 flag 置为 −1 并返回 True。

3. 若整个图 DFS 结束并未发现环,返回 true。

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> flag(numCourses,0);  //未访问
        vector<vector<int>> outpoints(numCourses);
        for(auto p:prerequisites)
            outpoints[p[1]].push_back(p[0]);
        for(int i=0;i<numCourses;i++)
            if(dfs(outpoints,flag,i)) return false;
        return true;
    }
    bool dfs(vector<vector<int>>& outpoints,vector<int>& flag,int i){
        if(flag[i]==-1) return false; //已被其他节点访问
        else if(flag[i]==1) return true;  //被本次节点重复访问
        flag[i]=1;
        for(auto op:outpoints[i])
            if(dfs(outpoints,flag,op)) return false;
        flag[i]=-1;
        return false;
    }
};

2. 课程安排的顺序

210. Course Schedule II (Medium)

https://leetcode-cn.com/problems/course-schedule-ii/

并查集

并查集可以动态地连通两个点,并且可以非常快速地判断两个点是否连通。

1. 冗余连接

684. Redundant Connection (Medium)

https://leetcode-cn.com/problems/redundant-connection/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值