图的数据结构表示、遍历

数据结构表示

// 模拟图中节点
public class Node {
    // 节点值
    public int value;
    // 节点入度
    public int in;
    // 节点出度
    public int out;
    // 与当前节点相邻的节点
    public List<Node> nexts;
    // 与当前节点相邻的边
    public List<Edge> edges;

    public Node(int value) {
        this.value = value;
        this.in = 0;
        this.out = 0;
        this.nexts = new ArrayList<>();
        this.edges = new ArrayList<>();
    }
}
// 模拟图中的边
public class Edge {
    // 边的权重
    public int weight;
    // 从哪个节点出发
    public Node from;
    // 指向哪个节点
    public Node to;

    public Edge(int weight, Node from, Node to) {
        this.weight = weight;
        this.from = from;
        this.to = to;
    }
}
// 模拟图结构
public class Graph {
    // 值与节点的对应
    public HashMap<Integer, Node> nodes;
    // 边的集合
    public HashSet<Edge> edges;

    public Graph() {
        nodes = new HashMap<>();
        edges = new HashSet<>();
    }
}

图的结构化实例

// 将给定的结构自定义转化为我们规定的图的数据结构模式
public class GraphGenerator {
    // 这里给一个实例
    // matrix:所有的边

    // N*3 的矩阵
    // [weight, from节点上面的值,to节点上面的值]
    // [ 5 , 0 , 7]
    // [ 3 , 0,  1]
    public static Graph createGraph(int[][] matrix) {
        Graph graph = new Graph();
        for (int i = 0; i < matrix.length; i++) {
            int weight = matrix[i][0];
            int from = matrix[i][1];
            int to = matrix[i][2];

            // 如果节点不存在,则创建
            if (!graph.nodes.containsKey(from)) {
                graph.nodes.put(from, new Node(from));
            }
            if (!graph.nodes.containsKey(to)) {
                graph.nodes.put(to, new Node(to));
            }

            // 取出图中的对应from to节点
            Node fromNode = graph.nodes.get(from);
            Node toNode = graph.nodes.get(to);
            // 创建两节点之间的边
            Edge newEdge = new Edge(weight, fromNode, toNode);
            // fromNode的相邻节点和边增加 出度加一 toNode的入度加一
            fromNode.out++;
            toNode.in++;
            fromNode.nexts.add(toNode);
            fromNode.edges.add(newEdge);
            // 图中的边增加
            graph.edges.add(newEdge);
        }
        return graph;
    }
}

BFS 广度优先遍历

在这里插入图片描述

public class BFS {
    public void bfs(Node start) {
        if (start == null) return;
        Queue<Node> queue = new LinkedList<>();
        Set<Node> set = new HashSet<>();
        queue.add(start);
        set.add(start);
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            System.out.println(node.value); // 对当前节点进行处理
            // 遍历它的相邻节点,但是要确保是没有被遍历过的,才加入队列
            for (Node next : node.nexts) {
                if (!set.contains(next)) {
                    queue.add(next);
                    set.add(next);
                }
            }
        }
    }
}

DFS 深度优先遍历

在这里插入图片描述

public class DFS {
    public void dfs(Node start) {
        if (start == null) return;
        Stack<Node> stack = new Stack<>();
        Set<Node> set = new HashSet<>();
        stack.push(start);
        set.add(start);
        System.out.println(start.value);
        while (!stack.isEmpty()) {
            Node node = stack.pop();
            // 遍历当前节点的相邻节点,当发现相邻节点没有被遍历过得时候
            // 将当前节点和这个相邻节点重新压入栈中,然后对这个相邻节点做相同的事情
            // stack中的元素就是当前深度优先遍历的路径
            // 每个元素被加入的时刻,就是这个元素执行操作的时候
            for (Node next : node.nexts) {
                if (!set.contains(next)) {
                    stack.push(node);
                    stack.push(next);
                    set.add(next);
                    System.out.println(next.value);
                    break;
                }
            }
        }
    }
}

图的拓扑排序

拓扑序列:一些活动,其中某些活动必须在另一些活动完成之后才能开始,一定是无环的有向图,称为AOV网。
拓扑排序:其实就是对一个有向图构造拓扑序列的过程。构造时会有两个结果:如果此网的全部结点都被输出,则说明其为不存在环的AOV网。如果没有输出全部顶点数,则说明这个网存在回路,不是AOV网。
拓扑排序基本思路:从AOV网中选择一个入度为0的顶点输出,然后删去此结点,并删除以此结点为尾的弧,继续重复此步骤,直到输出全部顶点或者AOV网中不存在入度为0的顶点为止。
在这里插入图片描述

public class Code03_TopologySort {
    public List<Node> sortedTopology(Graph graph) {

        // key:某个节点 value:剩余的入度
        Map<Node, Integer> inMap = new HashMap<>();
        // 入度为0的节点进入
        Queue<Node> zeroInQueue = new LinkedList<>();

        // 遍历所有节点,先将所有的节点加入到inMap中,入度为0的节点加入到队列中
        for (Node node : graph.nodes.values()) {
            inMap.put(node, node.in);
            if (node.in == 0) {
                zeroInQueue.add(node);
            }
        }

        List<Node> res = new ArrayList<>();
        // 进入拓扑序列的构造过程中
        while (!zeroInQueue.isEmpty()) {
            // 将入度为0的节点弹出
            Node zeroNode = zeroInQueue.poll();
            // 将节点为0的节点加入到结果集合中
            res.add(zeroNode);
            // 然后对这个节点的相邻节点进行遍历,将这些相邻节点的入度减一
            // 当发现入度为0时,加入队列
            for (Node next : zeroNode.nexts) {
                inMap.put(next, inMap.get(next) - 1);
                if (inMap.get(next) == 0) {
                    zeroInQueue.add(next);
                }
            }
        }
        return res;
    }
}

练码127. 拓扑排序

题目中给定的图的数据结构
在这里插入图片描述

class DirectedGraphNode {
    int label;
    List<DirectedGraphNode> neighbors;
    DirectedGraphNode(int x) {
        label = x;
        neighbors = new ArrayList<DirectedGraphNode>();
    }
}

BFS方式实现

import java.util.*;

public class Code03_TopologicalOrderBFS {
    // 不要提交这个类
    public static class DirectedGraphNode {
        public int label;
        public ArrayList<DirectedGraphNode> neighbors;

        public DirectedGraphNode(int x) {
            label = x;
            neighbors = new ArrayList<DirectedGraphNode>();
        }
    }

    public static ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
        // 创建一个节点与其入度对应的映射
        Map<DirectedGraphNode, Integer> indegreeMap = new HashMap<>();
        // 将所有的节点添加进入map中做一个初始化
        for (DirectedGraphNode directedGraphNode : graph) {
            indegreeMap.put(directedGraphNode, 0); // 初始每一个节点的入度为0
        }
        // 遍历所有节点,对每个节点的相邻节点的入度都加1
        for (DirectedGraphNode directedGraphNode : graph) {
            for (DirectedGraphNode neighbor : directedGraphNode.neighbors) {
                indegreeMap.put(neighbor, indegreeMap.get(neighbor) + 1);
            }
        }

        // 将所有入度为0的点放入队列中
        Queue<DirectedGraphNode> zeroInQueue = new LinkedList<>();
        for (DirectedGraphNode directedGraphNode : graph) {
            if (indegreeMap.get(directedGraphNode) == 0) {
                zeroInQueue.add(directedGraphNode);
            }
        }

        ArrayList<DirectedGraphNode> res = new ArrayList<>();
        // 遍历队列,进行拓扑排序
        while (!zeroInQueue.isEmpty()) {
            DirectedGraphNode cur = zeroInQueue.poll();
            res.add(cur);
            // 对其相邻节点的入度减一
            for (DirectedGraphNode neighbor : cur.neighbors) {
                indegreeMap.put(neighbor, indegreeMap.get(neighbor) - 1);
                if (indegreeMap.get(neighbor) == 0) {
                    zeroInQueue.add(neighbor);
                }
            }
        }
        return res;
    }
}

在这里插入图片描述

DFS方式第一种实现

import java.util.*;

public class Code03_TopologicalOrderDFS1 {
    public static class DirectedGraphNode {
        public int label;
        public ArrayList<DirectedGraphNode> neighbors;

        public DirectedGraphNode(int x) {
            label = x;
            neighbors = new ArrayList<DirectedGraphNode>();
        }
    }

    // 定义一个数据结构,属性为节点与其所对应的节点数
    public class Record {
        DirectedGraphNode node;
        long nodes;

        public Record(DirectedGraphNode node, long nodes) {
            this.node = node;
            this.nodes = nodes;
        }
    }

    // 自定义比较器,节点数越多越靠前
    public class MyComparator implements Comparator<Record> {

        @Override
        public int compare(Record o1, Record o2) {
            return o1.nodes == o2.nodes ? 0 : (o1.nodes > o2.nodes ? -1 : 1);
        }
    }


    public  ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
        HashMap<DirectedGraphNode, Record> order = new HashMap<>();
        // 计算所有节点的节点数
        for (DirectedGraphNode directedGraphNode : graph) {
            f(directedGraphNode, order);
        }

        ArrayList<Record> recordArr = new ArrayList<>();
        for (Record r : order.values()) {
            recordArr.add(r);
        }

        // 对Record记录按照节点数从大到小排序
        recordArr.sort(new MyComparator());
        ArrayList<DirectedGraphNode> res = new ArrayList<>();
        for (Record r : recordArr) {
            res.add(r.node);
        }
        return res;
    }

    // 统计传入节点的节点数
    // 当前来到cur点,请返回cur点所到之处,所有的点次!
    // 返回(cur,点次)
    // 缓存!!!!!order
    //  key : 某一个点的点次,之前算过了!
    //  value : 点次是多少
    public Record f(DirectedGraphNode cur, HashMap<DirectedGraphNode, Record> order) {
        if (order.containsKey(cur)) {
            return order.get(cur);
        }

        // 如果缓存中不存在这个cur, 说明他的点次还没有计算过
        long nodes = 0;
        for (DirectedGraphNode neighbor : cur.neighbors) {
            // 统计他的每一个相邻节点的点次,递归累加
            nodes += f(neighbor, order).nodes;
        }
        // 创建当前节点的Record
        Record record = new Record(cur, nodes + 1);
        // 加入缓存
        order.put(cur, record);
        return record;
    }
}

DFS第二种实现方式

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

public class Code03_TopologicalOrderDFS2 {
    public static class DirectedGraphNode {
        public int label;
        public ArrayList<DirectedGraphNode> neighbors;

        public DirectedGraphNode(int x) {
            label = x;
            neighbors = new ArrayList<DirectedGraphNode>();
        }
    }

    // 定义一个数据结构,属性为节点与其所对应的节点数
    public class Record {
        DirectedGraphNode node;
        int deep;

        public Record(DirectedGraphNode node, int deep) {
            this.node = node;
            this.deep = deep;
        }
    }

    // 自定义比较器,节点数越多越靠前
    public class MyComparator implements Comparator<Record> {

        @Override
        public int compare(Record o1, Record o2) {
            return o2.deep - o1.deep;
        }
    }

    public  ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
        HashMap<DirectedGraphNode, Record> order = new HashMap<>();
        for (DirectedGraphNode directedGraphNode : graph) {
            f(directedGraphNode, order);
        }

        ArrayList<Record> list = new ArrayList<>();
        for (DirectedGraphNode directedGraphNode : graph) {
            list.add(order.get(directedGraphNode));
        }

        list.sort(new MyComparator());

        ArrayList<DirectedGraphNode> res = new ArrayList<>();
        for (Record record : list) {
            res.add(record.node);
        }
        return res;
    }

    public Record f(DirectedGraphNode cur, Map<DirectedGraphNode, Record> order) {
        if (order.containsKey(cur)) {
            return order.get(cur);
        }

        int deep = Integer.MIN_VALUE;
        for (DirectedGraphNode neighbor : cur.neighbors) {
            deep = Math.max(deep, f(neighbor, order).deep);
        }

        Record record = new Record(cur, deep + 1);
        order.put(cur, record);
        return record;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值