图的存储方式
图的顶点结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class Node {
public int value;
public int out;
public int in;
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<>(); } }
|
图的边结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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; } }
|
图的邻接表结构
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Graph {
public HashMap<Integer, Node> nodes;
public HashSet<Edge> edges;
public Graph() { nodes = new HashMap<>(); edges = new HashSet<>(); } }
|
根据三元组生成图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| public class GraphGenerator {
public Graph createGraph(Integer[][] 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; } }
|
图的遍历
深度优先遍历
算法思想
方式1 非递归实现
- 利用栈实现
- 从源节点开始按照深度放入栈,然后弹出
- 每弹出一个节点,就把节点下一个没有进栈的邻节点放入栈
- 直到栈变空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public void dfs(Node node) { if (node == null) { return; }
Stack<Node> stack = new Stack<>(); HashSet<Node> set = new HashSet<>();
stack.add(node); set.add(node);
System.out.println(node.value);
while (!stack.isEmpty()) { Node cur = stack.pop(); for (Node next : cur.nexts) { if (!set.contains(next)) { stack.push(cur); stack.add(next); System.out.println(next.value); break; } } } }
|
方式2 递归实现
广度优先遍历
- 利用队列实现
- 从源节点开始依次按照宽度进队列,然后弹出
- 每弹出一个节点,就把该节点所有没有进过队列的邻节点放入队列
- 直到队列变空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public void bfs(Node node) { if (node == null) { return; }
Queue<Node> queue = new LinkedList<>(); HashSet<Object> set = new HashSet<>();
queue.offer(node); set.add(node);
while (!queue.isEmpty()) { Node cur = queue.poll(); System.out.println(cur.value);
for (Node next : cur.nexts) { if (!set.contains(next)) { set.add(next); queue.offer(next); } } } }
|
拓扑排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public List<Node> sortedTopology(Graph graph) { Map<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; }
|
Prim
前提:
要求无向图
比较器
1 2 3 4 5 6 7
| public class EdgeComparator implements Comparator<Edge> { @Override public int compare(Edge o1, Edge o2) { return o1.weight - o2.weight; } }
|
邻接表实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public Set<Edge> prim(Graph graph) { PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator()); HashSet<Node> set = new HashSet<>(); HashSet<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; }
|
邻接矩阵实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public int Prim(int[][] graph) { int size = graph.length; int[] distances = new int[size]; boolean[] visit = new boolean[size]; visit[0] = true; for (int i = 0; i < size; i++) { distances[i] = graph[0][i]; } int sum = 0; for (int i = 0; i < size; i++) { int minPath = Integer.MAX_VALUE; int minIndex = -1; for (int j = 0; j < size; j++) { if (!visit[j] && distances[j] < minPath) { minPath = distances[j]; minIndex = j; } } if (minIndex == -1) { return sum; } visit[minIndex] = true; sum += minPath; for (int j = 0; j < size; j++) { if (!visit[j] && distances[j] > graph[minIndex][j]) { distances[j] = graph[minIndex][j]; } } } return sum; }
|
Kruskal
并查集结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| public class UnionFind {
private HashMap<Node, Node> fatherMap;
private HashMap<Node, Integer> rankMap;
public UnionFind() { this.fatherMap = new HashMap<>(); this.rankMap = new HashMap<>(); }
private Node findFather(Node node) { Node father = fatherMap.get(node); if (father != node) { father = findFather(father); } fatherMap.put(node, 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); if (aFrank <= bFrank) { fatherMap.put(aFather, bFather); rankMap.put(bFather, aFrank + bFrank); } else { fatherMap.put(bFather, aFather); rankMap.put(aFather, aFrank + bFrank); } }
} }
|
比较器
1 2 3 4 5 6 7
| public class EdgeComparator implements Comparator<Edge> { @Override public int compare(Edge o1, Edge o2) { return o1.weight - o2.weight; } }
|
Kruskal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public Set<Edge> kruskal(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); } } return result; }
|
Dijkstra
原始版本
getMinDistanceAndUnselectedNode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public Node getMinDistanceAndUnselectedNode(HashMap<Node, Integer> distanceMap, HashSet<Node> touchedNodes) {
Node minNode = null; int minDistance = Integer.MAX_VALUE; for (Map.Entry<Node, Integer> entry : distanceMap.entrySet()) { Node node = entry.getKey(); int distance = entry.getValue(); if (!touchedNodes.contains(node) && distance < minDistance) { minNode = node; minDistance = distance; } } return minNode; }
|
Dijkstra
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
public HashMap<Node, Integer> dijkstra(Node head, int size) {
HashMap<Node, Integer> distanceMap = new HashMap<>(); distanceMap.put(head, 0); HashSet<Node> selectedNodes = new HashSet<>(); Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes); while (minNode != null) { int distance = distanceMap.get(minNode); for (Edge edge : minNode.edges) { Node toNode = edge.to; if (!distanceMap.containsKey(toNode)) { distanceMap.put(toNode, distance + edge.weight); } else { distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight)); } selectedNodes.add(minNode); minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes); } } return distanceMap; }
|
优化版本
存在问题 对于dist数组每次挑选最小距离改用堆存储,需要手动改写堆,降低该操作的时间复杂度
系统提供的堆无法做到的事情:
- 已经入堆的元素,如果参与排序的指标方法变化,系统提供的堆无法做到时间复杂度O(logN)调整!都是O(N)的调整!
- 系统提供的堆只能弹出堆顶,做不到自由删除任何一个堆中的元素,或者说,无法在时间复杂度O(logN)内完成!一定会高于O(logN)
- 在一个已经组织好的大根堆或者小根堆中,更改其中的某个值后,仍要求其为大根堆或者小根堆 ,系统堆是无法实现的
- 根本原因::无反向索引表
手写堆
定义结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private Node[] nodes;
private HashMap<Node, Integer> heapIndexMap;
private HashMap<Node, Integer> distanceMap;
private int size;
public NodeHeap(int size) { this.nodes = new Node[size]; this.heapIndexMap = new HashMap<>(); this.distanceMap = new HashMap<>(); this.size = 0; }
|
判断堆空
1 2 3 4 5 6 7
|
public boolean isEmpty() { return size == 0; }
|
弹出堆顶元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public NodeRecord pop() { NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0])); swap(0, size - 1);
heapIndexMap.put(nodes[size - 1], -1); distanceMap.remove(nodes[size - 1]); nodes[size - 1] = null; heapify(0, --size); return nodeRecord; }
|
堆结构变化后调整
1 2 3 4 5 6 7 8 9 10 11 12
|
private void insertHeapify(Node node, int index) { while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) { swap(index, (index - 1) / 2); index = (index - 1) / 2; } }
|
就地调整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public void heapify(int index, int size) { int left = index * 2 + 1; while (left < size) { int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left]) ? left + 1 : left; smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index; if (smallest == index) { break; } swap(smallest, index); index = smallest; left = index * 2 + 1; } }
|
是否进过堆
1 2 3 4 5 6 7 8
|
private boolean isEntered(Node node) { return heapIndexMap.containsKey(node); }
|
是否在堆上
1 2 3 4 5 6 7 8
|
private boolean inHeap(Node node) { return isEntered(node) && heapIndexMap.get(node) != -1; }
|
交换堆上的节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
private void swap(int index1, int index2) { heapIndexMap.put(nodes[index1], index2); heapIndexMap.put(nodes[index2], index1); Node tmp = nodes[index1]; nodes[index1] = nodes[index2]; nodes[index2] = tmp; }
|
决定node是否并入路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public void addOrUpdateOrIgnore(Node node, int distance) { if (inHeap(node)) { distanceMap.put(node, Math.min(distanceMap.get(node), distance)); insertHeapify(node, heapIndexMap.get(node)); } if (!isEntered(node)) { nodes[size] = node; heapIndexMap.put(node, size); distanceMap.put(node, distance); insertHeapify(node, size++); } }
|
Dijkstra优化算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
public static HashMap<Node, Integer> dijkstra2(Node head, int size) { NodeHeap nodeHeap = new NodeHeap(size); nodeHeap.addOrUpdateOrIgnore(head,0); HashMap<Node, Integer> result = new HashMap<>(); while (!nodeHeap.isEmpty()) { NodeRecord record = nodeHeap.pop(); Node cur = record.node; int distance = record.distance;
for (Edge edge : cur.edges) { nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance); } result.put(cur, distance); } return result; }
|
Floyd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public class Code7Floyd { public static void Floyd(int[][] graph, int[][] path) { int i, j, k; int[][] temp = new int[graph.length][graph.length]; for (i = 0; i < graph.length; i++) { for (j = 0; j < graph.length; j++) { path[i][j] = -1; temp[i][j] = graph[i][j]; } }
for (i = 0; i < graph.length; i++) { for (j = 0; j < graph.length; j++) { for (k = 0; k < graph.length; k++) { if (temp[i][j] > temp[i][k] + temp[k][j]) { temp[i][j] = temp[i][k] + temp[k][j]; path[i][j] = k; } } } } } }
|