LeetCode-数据结构-图

二分图

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

785 判断是否为二分图

着色问题用深度遍历的方法借助栈非循环进行,考虑是非连通情况所以要对图中的每一个节点都进行遍历。

     public boolean isBipartite(int[][] graph) {
        if (graph == null || graph.length == 0) return false;
        int v = graph.length;
        int[] colors = new int[v];  // 0未被染色, 1黑  2白
        // 要考虑非连通图, 所以要遍历每一个结点
        for (int i = 0; i < v; i++) {
            // lastColor为0
            if (!dfs(graph, i, colors, 0)) return false;
        }
        return true;
    }
    private boolean dfs(int[][] graph, int i, int[] colors, int lastColor) {
        // 注意,被染色的就不要继续染色了(因为这是自底向上的,被染色的点,其相连的节点肯定被染色了)
        // 如果继续对被染色的节点染色,就会导致死循环
        if (colors[i] != 0) return colors[i] != lastColor;
        // 未被染色,染成与相邻结点不同的颜色(lastColor为0时,就染成1)
        colors[i] = lastColor == 1 ? 2 : 1;
        for (int j = 0; j < graph[i].length; j++) {
            if (!dfs(graph, graph[i][j], colors, colors[i])) return false;
        }
        return true;
    }

借用栈进行该过程

class Solution {    public boolean isBipartite(int[][] graph) {        int n = graph.length;        int[] color = new int[n];        Arrays.fill(color, -1);
        for (int start = 0; start < n; ++start) {            if (color[start] == -1) {                Stack<Integer> stack = new Stack();                stack.push(start);                color[start] = 0;
                while (!stack.empty()) {                    Integer node = stack.pop();                    for (int nei: graph[node]) {                        if (color[nei] == -1) {                            stack.push(nei);                            color[nei] = color[node] ^ 1;                        } else if (color[nei] == color[node]) {                            return false;                         }                    }                }            }        }
        return true;    }}

拓扑排序

207 课程安排的合法性

图的邻接表和邻接矩阵表示方法
DFS BFS
拓扑排序
该题只需要判断在有向图中是否存在环
详细讲解
使用拓扑排序方法 广度优先角度

class Solution {    public boolean canFinish(int numCourses, int[][] prerequisites) {         int[] dp=new int[numCourses];         for(int[] pre:prerequisites) dp[pre[0]]++;         LinkedList<Integer> queue=new LinkedList<>();         for(int i=0;i<numCourses;i++){             if(dp[i]==0) queue.addLast(i);         }         while(!queue.isEmpty()){             Integer flag=queue.removeFirst();             numCourses--;             for(int[] pre:prerequisites){                 if(pre[1]!=flag) continue;                 if(--dp[pre[0]]==0) queue.add(pre[0]);             }         }         return numCourses==0;    }}

深度优先解决
需要用标记位来表示是否被访问过
有三种状态:未被访问过
被当前元素访问过
被其他元素访问过
当遇到的元素未被访问过,直接标记已被当前元素访问过。
当遇到元素被其他元素访问过,则直接证明无环。
当遇到哦元素被当前元素访问过,则直接返回有环。
可以用三个值来分别标记当前的状态,也可以使用两个状态量,一个用来标记当前元素访问情况,一个用来标记其他元素访问情况。

public boolean canFinish(int numCourses, int[][] prerequisites) {
    List<Integer>[] graphic = new List[numCourses];
    for (int i = 0; i < numCourses; i++) {
        graphic[i] = new ArrayList<>();
    }
    for (int[] pre : prerequisites) {
        graphic[pre[0]].add(pre[1]);
    }
    boolean[] globalMarked = new boolean[numCourses];
    boolean[] localMarked = new boolean[numCourses];
    for (int i = 0; i < numCourses; i++) {
        if (hasCycle(globalMarked, localMarked, graphic, i)) {
            return false;
        }
    }
    return true;
}

private boolean hasCycle(boolean[] globalMarked, boolean[] localMarked,
                         List<Integer>[] graphic, int curNode) {

    if (localMarked[curNode]) {
        return true;
    }
    if (globalMarked[curNode]) {
        return false;
    }
    globalMarked[curNode] = true;
    localMarked[curNode] = true;
    for (int nextNode : graphic[curNode]) {
        if (hasCycle(globalMarked, localMarked, graphic, nextNode)) {
            return true;
        }
    }
    localMarked[curNode] = false;
    return false;
}

注意标志量的访问顺序。

210 课程安排顺序

用一个栈结构对数据进行记录

public int[] findOrder(int numCourses, int[][] prerequisites) {
    List<Integer>[] graphic = new List[numCourses];
    for (int i = 0; i < numCourses; i++) {
        graphic[i] = new ArrayList<>();
    }
    for (int[] pre : prerequisites) {
        graphic[pre[0]].add(pre[1]);
    }
    Stack<Integer> postOrder = new Stack<>();
    boolean[] globalMarked = new boolean[numCourses];
    boolean[] localMarked = new boolean[numCourses];
    for (int i = 0; i < numCourses; i++) {
        if (hasCycle(globalMarked, localMarked, graphic, i, postOrder)) {
            return new int[0];
        }
    }
    int[] orders = new int[numCourses];
    for (int i = numCourses - 1; i >= 0; i--) {
        orders[i] = postOrder.pop();
    }
    return orders;
}

private boolean hasCycle(boolean[] globalMarked, boolean[] localMarked, List<Integer>[] graphic,
                         int curNode, Stack<Integer> postOrder) {

    if (localMarked[curNode]) {
        return true;
    }
    if (globalMarked[curNode]) {
        return false;
    }
    globalMarked[curNode] = true;
    localMarked[curNode] = true;
    for (int nextNode : graphic[curNode]) {
        if (hasCycle(globalMarked, localMarked, graphic, nextNode, postOrder)) {
            return true;
        }
    }
    localMarked[curNode] = false;
    postOrder.push(curNode);
    return false;
}

并查集

684 冗余连接

并查集可以动态地连通两个点,并且可以非常快速地判断两个点是否连通。
算法思想理解
遍历所有的边将连通的点放到同一集合,构成连通分量。在遍历过程中如果某一条边的两个点属于同一连通分量,那么该边是冗余的。

public int[] findRedundantConnection(int[][] edges) {
    int N = edges.length;
    UF uf = new UF(N);
    for (int[] e : edges) {
        int u = e[0], v = e[1];
        if (uf.connect(u, v)) {
            return e;
        }
        uf.union(u, v);
    }
    return new int[]{-1, -1};
}

private class UF {

    private int[] id;

    UF(int N) {
        id = new int[N + 1];
        for (int i = 0; i < id.length; i++) {
            id[i] = i;
        }
    }

    void union(int u, int v) {
        int uID = find(u);
        int vID = find(v);
        if (uID == vID) {
            return;
        }
        for (int i = 0; i < id.length; i++) {
            if (id[i] == uID) {
                id[i] = vID;
            }
        }
    }

    int find(int p) {
        return id[p];
    }

    boolean connect(int u, int v) {
        return find(u) == find(v);
    }
}

部分解答思路参考:https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E5%9B%BE.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值