数据结构表示
// 模拟图中节点
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;
}
}