基础算法之图
这里是基础算法系列的第四部分
前言
本文章最主要介绍图的各个问题。
二叉树相关问题
图的组成部分
结点Node
结点Node:值,入度,出度,下一个结点集合,相连的边集合
public class Node {
public int value;
public int in;
public int out;
public ArrayList<Node> nexts;
public ArrayList<Edge> edges;
public Node(int value) {
this.value = value;
in = 0;
out = 0;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
边 Edge
权重,来自结点,目的结点
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;
}
图 Graph
图Graph:结点集合,边集合
public class Graph {
public HashMap<Integer,Node> nodes;
public HashSet<Edge> edges;
public Graph() {
nodes = new HashMap<>();
edges = new HashSet<>();
}
}
图的生产器GraphGenerator
根据边矩阵matrix边矩阵,每一个边代表一行,每行是权重,来自点,目标点
生成我们需要处理的结构—>图
public static Graph createGraph(Integer[][] matrix) {
//matrix是一个边矩阵,每一个边代表一行,每行是权重,来自点,目标点
Graph graph = new Graph();
for (int i = 0; i < matrix.length; i++) {
Integer weight = matrix[i][0];
Integer from = matrix[i][1];
Integer 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));
}
//根据值对结点映射
Node fromNode = graph.nodes.get(from);
Node toNode = graph.nodes.get(to);
//由两相连的结点新建边
Edge newEdge = new Edge(weight, fromNode, toNode);
//把相邻的结点添加进去
fromNode.nexts.add(toNode);
//对来自结点增加出度
fromNode.out++;
//目标结点增加入度
toNode.in++;
//在两个相邻的结点添加边
fromNode.edges.add(newEdge);
graph.edges.add(newEdge);
}
return graph;
}
拓扑排序
总共两个结构,:①结点映射入度数,②零入度队列。
public class Code03_TopologySort {
// directed graph and no loop
public static List<Node> sortedTopology(Graph graph) {
HashMap<Node, Integer> inMap = new HashMap<>();
Queue<Node> zeroInQueue = new LinkedList<>();
for (Node node : graph.nodes.values()) {
inMap.put(node, node.in);
if (node.in == 0) {
zeroInQueue.add(node);
}
}
List<Node> result = new ArrayList<>();
while (!zeroInQueue.isEmpty()) {
Node cur = zeroInQueue.poll();
result.add(cur);
for (Node next : cur.nexts) {
inMap.put(next, inMap.get(next) - 1);
if (inMap.get(next) == 0) {
zeroInQueue.add(next);
}
}
}
return result;
}
并查集结构
组成:①哈希表<node,最顶点父节点>。②哈希表<Node,rank>(父节点是最大的)
方法:①findFather,找某个结点的父结点,②makeSet把结点集合初始化为基础的并查集结构,③union:把两个结点做并集。
public static class UnionFind {
private HashMap<Node, Node> fatherMap;
private HashMap<Node, Integer> rankMap;
public UnionFind() {
fatherMap = new HashMap<Node, Node>();
rankMap = new HashMap<Node, Integer>();
}
//找父顶级结点
private Node findFather(Node n) {
Node father = fatherMap.get(n);
if (father != n) {
father = findFather(father);
}
fatherMap.put(n, father);
return father;
}
//初始化一个集合,对父集合,等级集合进行清零,并初始化每个结点
public void makeSets(Collection<Node> nodes) {
fatherMap.clear();
rankMap.clear();
for (Node node : nodes) {
fatherMap.put(node, node);
rankMap.put(node, 1);
}
}
//判断是否在一个集合内
public boolean isSameSet(Node a, Node b) {
return findFather(a) == findFather(b);
}
//做并集
public void union(Node a, Node b) {
if (a == null || b == null) {
return;
}
Node aFather = findFather(a);
Node bFather = findFather(b);
if (aFather != bFather) {
int aFrank = rankMap.get(aFather);
int bFrank = rankMap.get(bFather);
//若a的长度小于等于b的长度
if (aFrank <= bFrank) {
fatherMap.put(aFather, bFather);
rankMap.put(bFather, aFrank + bFrank);
} else {
fatherMap.put(bFather, aFather);
rankMap.put(aFather, aFrank + bFrank);
}
}
}
}
Kruskal算法
在并查集的基础上,最小连通性,则是没有回环,则是看一条边的两个点是否在一个集合内
public static Set<Edge> kruskalMST(Graph graph) {
UnionFind unionFind = new UnionFind();
unionFind.makeSets(graph.nodes.values());
PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
//把图的边进行排序
for (Edge edge : graph.edges) {
priorityQueue.add(edge);
}
//最小连通性,则是没有回环,则是看一条边的两个点是否在一个集合内
Set<Edge> result = new HashSet<>();
while (!priorityQueue.isEmpty()) {
Edge edge = priorityQueue.poll();
if (!unionFind.isSameSet(edge.from, edge.to)) {
result.add(edge);
unionFind.union(edge.from, edge.to);
}
}
return result;
}
prim算法
其中三个结构:优先队列
public static Set<Edge> primMST(Graph graph) {
PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(
new EdgeComparator());
HashSet<Node> set = new HashSet<>();
Set<Edge> result = new HashSet<>();
for (Node node : graph.nodes.values()) {
if (!set.contains(node)) {
set.add(node);
for (Edge edge : node.edges) {
priorityQueue.add(edge);
}
while (!priorityQueue.isEmpty()) {
Edge edge = priorityQueue.poll();
Node toNode = edge.to;
if (!set.contains(toNode)) {
set.add(toNode);
result.add(edge);
for (Edge nextEdge : toNode.edges) {
priorityQueue.add(nextEdge);
}
}
}
}
}
return result;
}