【戀上數據結構】圖代碼實現、最小生成樹(Prim、Kruskal)、最短路徑(Dijkstra、Bellman-Ford、Floyd)

最小生成樹(Minimum Spanning Tree)

生成樹(Spanning Tree),也稱爲支撐樹

  • 連通圖的極小連通子圖,它含有圖中全部的 n 個頂點,恰好只有 n – 1 條邊

在這裏插入圖片描述

最小生成樹(Minimum Spanning Tree,簡稱MST)

  • 也稱爲最小權重生成樹(Minimum Weight Spanning Tree)、最小支撐樹
  • 是所有生成樹中,總權值最小的那棵
  • 適用於有權的連通圖(無向)

在這裏插入圖片描述
最小生成樹在許多領域都有重要的作用,例如:

  • 要在 n 個城市之間鋪設光纜,使它們都可以通信
  • 鋪設光纜的費用很高,且各個城市之間因爲距離不同等因素,鋪設光纜的費用也不同
  • 如何使鋪設光纜的總費用最低?—— 最小生成樹的應用

如果圖的每一條邊的權值都互不相同,那麼最小生成樹將只有一個,否則可能會有多個最小生成樹

求最小生成樹的2個經典算法:

  • Prim(普里姆算法)
  • Kruskal(克魯斯克爾算法)

Prim算法

切分定理

切分(Cut)

  • 把圖中的節點分爲兩部分,稱爲一個切分
    下圖有個切分 C = (S, T),S = { A, B, D },T = { C, E }

在這裏插入圖片描述 橫切邊(Crossing Edge)

  • 如果一個邊的兩個頂點,分別屬於切分的兩部分,這個邊稱爲橫切邊
    比如上圖的邊 BC、BE、DE 就是橫切邊

切分定理給定任意切分,橫切邊中權值最小的邊必然屬於最小生成樹

Prim算法 – 執行過程

假設 G = (V,E) 是有權的連通圖(無向),A 是 G 中最小生成樹的邊集

  • 算法從 S = { u0 }(u0 ∈ V),A = { } 開始,重複執行下述操作,直到 S = V 爲止
    找到切分 C = (S,V – S) 的最小橫切邊 (u0,v0) 併入集合 A,同時將 v0 併入集合 S

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

Prim算法 – 代碼實現

public Set<EdgeInfo<V, E>> mst() {
	return prim();
}
private Set<EdgeInfo<V,	 E>> prim(){
	// 最小生成樹的頂點數量爲: 圖的頂點數 1
	int verticesSize = vertices.size();
	Iterator<Vertex<V, E>> it = vertices.values().iterator(); // map的迭代器
	if(!it.hasNext()) return null;
	
	Set<EdgeInfo<V, E>> edgeInfos = new HashSet<>();
	Set<Vertex<V, E>> addedVertices = new HashSet<>(); // 標記已經添加的頂點
	Vertex<V, E> vertex = it.next(); // 隨機取出一個頂點
	addedVertices.add(vertex);

	MinHeap<Edge<V, E>> heap = new MinHeap<>(vertex.outEdges, edgeComparator);
	
	while(!heap.isEmpty() && addedVertices.size() < verticesSize){
		Edge<V, E> edge = heap.remove();
		if(addedVertices.contains(edge.to)) continue;
		
		edgeInfos.add(edge.info());
		addedVertices.add(edge.to);
		heap.addAll(edge.to.outEdges);
	}
	return edgeInfos;
	
}

Kruskal算法

Kruskal算法 – 執行過程

按照邊的權重順序(從小到大)將邊加入生成樹中,直到生成樹中含有 V–1 條邊爲止( V 是頂點數量)

  • 若加入該邊會與生成樹形成環,則不加入該邊
  • 從第 3 條邊開始,可能會與生成樹形成環

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

Kruskal算法 – 代碼實現

需要用到並查集數據結構,用來判斷加入的邊是否會形成環。

public Set<EdgeInfo<V, E>> mst() {
	return kruskal();
}

private Set<EdgeInfo<V,	 E>> kruskal(){
	// 最小生成樹的頂點數量爲: 圖的頂點數 1
	int edgeSize = vertices.size() - 1;
	if(edgeSize == -1) return null; // 空的圖
	
	Set<EdgeInfo<V, E>> edgeInfos = new HashSet<>();
	MinHeap<Edge<V, E>> heap = new MinHeap<>(edges, edgeComparator);
	// 並查集用來判斷加入的邊是否會形成環
	UnionFind<Vertex<V, E>> uf = new UnionFind<>(); 
	// 初始化並查集,將其中每個元素設置爲單獨的集合
	vertices.forEach((V v, Vertex<V, E> vertex) -> {
		uf.makeSet(vertex);
	});
	
	while(!heap.isEmpty() && edgeInfos.size() < edgeSize){
		Edge<V, E> edge = heap.remove();
		if(uf.isSame(edge.from, edge.to)) continue;
		
		edgeInfos.add(edge.info());
		uf.union(edge.from, edge.to);
	}
	return edgeInfos;

}

最短路徑(Shortest Path)

最短路徑是指兩頂點之間權值之和最小的路徑(有向圖、無向圖均適用,不能有負權環

有向圖:
在這裏插入圖片描述

無向圖:
在這裏插入圖片描述

最短路徑的典型應用之一:路徑規劃問題

求解最短路徑的3個經典算法:

  • 單源最短路徑算法
    • Dijkstra(迪傑斯特拉算法)
    • Bellman-Ford(貝爾曼-福特算法)
  • 多源最短路徑算法
    • Floyd(弗洛伊德算法)

最短路徑 – 概念

無權圖

無權圖相當於是全部邊權值爲1的有權圖
在這裏插入圖片描述

負權邊

有負權邊,但沒有負權環時,存在最短路徑
在這裏插入圖片描述
A到E的最短路徑是:A → B → E

負權環

有負權環時,不存在最短路徑
在這裏插入圖片描述
通過負權環, A到E的路徑可以無限短
A → E → D → F → E → D → F → E → D → F → E → D → F → E → …

Dijkstra

Dijkstra 屬於單源最短路徑算法,用於計算一個頂點到其他所有頂點的最短路徑

  • 使用前提:不能有負權邊
  • 時間複雜度:可優化至 O(ElogV) ,E 是邊數量,V 是節點數量

Dijkstra – 等價思考

在這裏插入圖片描述

Dijkstra – 執行過程

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

Dijkstra – 代碼實現版本1(只返回每條最短路徑的總權值)

接口文件 Graph.java:

package com.mj.graph;

import java.util.List;
import java.util.Map;
import java.util.Set;

public abstract class Graph<V, E> {
	protected WeightManager<E> weightManager; // 權重管理
	
	public Graph() {}
	
	public Graph(WeightManager<E> weightManager) {
		this.weightManager = weightManager;
	}
	
	public abstract int edgesSize(); // 邊的數量
	public abstract int verticesSize();	// 頂點數量
	
	public abstract void addVertex(V v); // 添加頂點
	public abstract void addEdge(V from, V to); // 添加邊
	public abstract void addEdge(V from, V to, E weight);// 添加邊
	
	public abstract void removeVertex(V v); // 刪除頂點
	public abstract void removeEdge(V from, V to); // 刪除邊
	
	public abstract void bfs(V begin, VertexVisitor<V> visitor); // 廣度優先搜索
	public abstract void dfs(V begin, VertexVisitor<V> visitor); // 深度優先搜索
	
	public abstract List<V> topologicalSort(); // 拓撲排序
	
	public abstract Set<EdgeInfo<V, E>> mst(); // 最小生成樹
	
	public abstract Map<V, E> shortestPath(V begin); // 最短路徑
	
	public interface WeightManager<E> { // 管理權重
		int compare(E w1, E w2); // 比較權重
		E add(E w1, E w2); // 權重相加
		E zero(); 
	}
	
	public interface VertexVisitor<V>{
		boolean visit(V v);
	}
	
	public static class EdgeInfo<V, E>{
		private V from;
		private V to;
		private E weight;
		public EdgeInfo(V from, V to, E weight) { // 邊信息
			super();
			this.from = from;
			this.to = to;
			this.weight = weight;
		}
		public V getFrom() {
			return from;
		}
		public void setFrom(V from) {
			this.from = from;
		}
		public V getTo() {
			return to;
		}
		public void setTo(V to) {
			this.to = to;
		}
		public E getWeight() {
			return weight;
		}
		public void setWeight(E weight) {
			this.weight = weight;
		}
		@Override
		public String toString() {
			return "EdgeInfo [from=" + from + ", to=" + to + ", weight=" + weight + "]";
		}
	}
	
 }
@Override
public Map<V, E> shortestPath(V begin) {
	Vertex<V, E> beginVertex = vertices.get(begin); // 源點
	if(beginVertex == null) return null;
	
	Map<V, E> selectedPaths = new HashMap<>(); // 最終的最短路徑
	Map<Vertex<V, E>, E> paths = new HashMap<>(); // 當前最短路徑
	// 初始化paths
	for(Edge<V, E> edge : beginVertex.outEdges){
		paths.put(edge.to, edge.weight);
	}
	
	while(!paths.isEmpty()){
		Entry<Vertex<V, E>, E> minEntry = getMinPath(paths); // 挑選出當前最短路徑中最短的點
		// minVertex 離開桌面,被確定爲最終的最短路徑
		Vertex<V, E> minVertex = minEntry.getKey();
		selectedPaths.put(minVertex.value, minEntry.getValue());
		paths.remove(minVertex);
		// 對它的minVertex的outEdges進行鬆弛操作
		for(Edge<V, E> edge : minVertex.outEdges){
			// 如果edge.to已經離開桌面,就沒必要進行鬆弛操作
			if(selectedPaths.containsKey(edge.to.value) || edge.to.equals(beginVertex)) continue;
			// 新的可選擇的最短路徑:beginVertex到edge.from的最短路徑 + edge.weight
			E newWeight = weightManager.add(minEntry.getValue(), edge.weight);
			// 以前的最短路徑:beginVertex到edge.to的最短路徑
			E oldWeight = paths.get(edge.to);
			if(oldWeight == null || weightManager.compare(newWeight, oldWeight) < 0) {
				paths.put(edge.to, newWeight);
			}
		}
	}
	return selectedPaths;
}
/**
 * 從paths中挑一個最小的路徑出來
 */
private Entry<Vertex<V, E>, E> getMinPath(Map<Vertex<V, E>, E> paths) {
	Iterator<Entry<Vertex<V, E>, E>> it = paths.entrySet().iterator();
	Entry<Vertex<V, E>, E> minEntry = it.next();
	while (it.hasNext()) {
		Entry<Vertex<V, E>, E> entry = it.next();
		if (weightManager.compare(entry.getValue(), minEntry.getValue()) < 0) {
			minEntry = entry;
		}
	}
	return minEntry;
}

Dijkstra – 代碼實現版本2(返回最短路徑的總權值和邊信息)

接口文件 Graph.java

接口文件 Graph.java:修改了 shortestPath方法接口,增加了 PathInfo 內部類。

package com.mj.graph;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public abstract class Graph<V, E> {
	protected WeightManager<E> weightManager; // 權重管理
	
	public Graph() {}
	
	public Graph(WeightManager<E> weightManager) {
		this.weightManager = weightManager;
	}
	
	public abstract int edgesSize(); // 邊的數量
	public abstract int verticesSize();	// 頂點數量
	
	public abstract void addVertex(V v); // 添加頂點
	public abstract void addEdge(V from, V to); // 添加邊
	public abstract void addEdge(V from, V to, E weight);// 添加邊
	
	public abstract void removeVertex(V v); // 刪除頂點
	public abstract void removeEdge(V from, V to); // 刪除邊
	
	public abstract void bfs(V begin, VertexVisitor<V> visitor); // 廣度優先搜索
	public abstract void dfs(V begin, VertexVisitor<V> visitor); // 深度優先搜索
	
	public abstract List<V> topologicalSort(); // 拓撲排序
	
	public abstract Set<EdgeInfo<V, E>> mst(); // 最小生成樹
	
//	public abstract Map<V, E> shortestPath(V begin); // 最短路徑
	public abstract Map<V, PathInfo<V, E>> shortestPath(V begin); // 返回路徑信息的最短路徑
	
	public interface WeightManager<E> { // 管理權重
		int compare(E w1, E w2); // 比較權重
		E add(E w1, E w2); // 權重相加
		E zero(); 
	}
	
	public interface VertexVisitor<V>{
		boolean visit(V v);
	}
	
	/**
	 * 最短路徑返回的路徑信息, 包含到某個頂點的路徑信息和總權值
	 */
	public static class PathInfo<V, E> {
		protected E weight; // 權值
		protected List<EdgeInfo<V, E>> edgeInfos = new LinkedList<>(); 
		public PathInfo() {}
		public PathInfo(E weight) {
			this.weight = weight;
		}
		public E getWeight() {
			return weight;
		}
		public void setWeight(E weight) {
			this.weight = weight;
		}
		public List<EdgeInfo<V, E>> getEdgeInfos() {
			return edgeInfos;
		}
		public void setEdgeInfos(List<EdgeInfo<V, E>> edgeInfos) {
			this.edgeInfos = edgeInfos;
		}
		@Override
		public String toString() {
			return "PathInfo [weight=" + weight + ", edgeInfos=" + edgeInfos + "]";
		}
		
	}
	
	public static class EdgeInfo<V, E>{
		private V from;
		private V to;
		private E weight;
		public EdgeInfo(V from, V to, E weight) { // 邊信息
			super();
			this.from = from;
			this.to = to;
			this.weight = weight;
		}
		public V getFrom() {
			return from;
		}
		public void setFrom(V from) {
			this.from = from;
		}
		public V getTo() {
			return to;
		}
		public void setTo(V to) {
			this.to = to;
		}
		public E getWeight() {
			return weight;
		}
		public void setWeight(E weight) {
			this.weight = weight;
		}
		@Override
		public String toString() {
			return "EdgeInfo [from=" + from + ", to=" + to + ", weight=" + weight + "]";
		}
	}
	
 }

dijkstra 實現1

@Override
public Map<V, PathInfo<V, E>> shortestPath(V begin) {
	return dijkstra(begin);
}

private Map<V, PathInfo<V, E>> dijkstra(V begin) {
	Vertex<V, E> beginVertex = vertices.get(begin);
	if(beginVertex == null) return null;
	
	Map<V, PathInfo<V, E>> selectedPaths = new HashMap<>(); // 最終的最短路徑
	Map<Vertex<V, E>, PathInfo<V, E>> paths = new HashMap<>(); // 當前的最短路徑

	// 初始化paths
	for (Edge<V, E> edge : beginVertex.outEdges) {
		PathInfo<V, E> path = new PathInfo<>();
		path.weight = edge.weight;
		path.edgeInfos.add(edge.info());
		paths.put(edge.to, path);
	}
	
	while(!paths.isEmpty()) {
		// 挑選出當前最短路徑中最短的點
		Entry<Vertex<V, E>, PathInfo<V, E>> minEntry = getMinPath(paths);
		// minVertex 離開桌面,被確定爲最終的最短路徑
		Vertex<V, E> minVertex = minEntry.getKey();
		selectedPaths.put(minVertex.value, minEntry.getValue()); // 放入最終的最短路徑
		paths.remove(minVertex); // 從當前的最短路徑中移除
		
		// 對離開桌面的minVertex的outEdges進行鬆弛操作
		for (Edge<V, E> edge : minVertex.outEdges) {
			// 如果edge.to已經離開桌面, 就沒有必要進行鬆弛操作
			if(selectedPaths.containsKey(edge.to.value)) continue;
			// 新的可選擇的最短路徑: beginVertex到edge.from的最短路徑 + edge.weight
			E newWeight = weightManager.add(minEntry.getValue().weight, edge.weight);
			// 以前的最短路徑: beginVertex到edge.to的最短路徑
			PathInfo<V, E> oldPath = paths.get(edge.to);
			if(oldPath == null || weightManager.compare(newWeight, oldPath.weight) < 0) {
				PathInfo<V, E> path = new PathInfo<>();
				path.weight = newWeight;
				path.edgeInfos.addAll(minEntry.getValue().edgeInfos);
				path.edgeInfos.add(edge.info());
				paths.put(edge.to, path);
			}
		}
	}
	selectedPaths.remove(begin);
	return selectedPaths;
}
/**
 * 從paths中挑一個最小的路徑出來(遍歷)
 * 可用小頂堆進行優化
 */
private Entry<Vertex<V, E>, PathInfo<V, E>> getMinPath(Map<Vertex<V, E>, PathInfo<V, E>> paths) {
	Iterator<Entry<Vertex<V, E>, PathInfo<V, E>>> it = paths.entrySet().iterator();
	Entry<Vertex<V, E>, PathInfo<V, E>> minEntry = it.next();
	while (it.hasNext()) {
		Entry<Vertex<V, E>, PathInfo<V, E>> entry = it.next();
		if (weightManager.compare(entry.getValue().weight, minEntry.getValue().weight) < 0) {
			minEntry = entry;
		}
	}
	return minEntry;
}

dijkstra 實現2(封裝了relax鬆弛操作)

private Map<V, PathInfo<V, E>> dijkstra(V begin) {
	Vertex<V, E> beginVertex = vertices.get(begin);
	if(beginVertex == null) return null;
	
	Map<V, PathInfo<V, E>> selectedPaths = new HashMap<>(); // 最終的最短路徑
	Map<Vertex<V, E>, PathInfo<V, E>> paths = new HashMap<>(); // 當前的最短路徑
	paths.put(beginVertex, new PathInfo<>(weightManager.zero()));
	// 初始化paths
//		for (Edge<V, E> edge : beginVertex.outEdges) { // 遍歷源點出去的邊, 添加到當前的最短路徑中
//			PathInfo<V, E> path = new PathInfo<>();
//			path.weight = edge.weight;
//			path.edgeInfos.add(edge.info());
//			paths.put(edge.to, path);
//		}
	
	while(!paths.isEmpty()) {
		// 挑選出當前最短路徑中最短的點
		Entry<Vertex<V, E>, PathInfo<V, E>> minEntry = getMinPath(paths);
		// minVertex 離開桌面,被確定爲最終的最短路徑
		Vertex<V, E> minVertex = minEntry.getKey();
		PathInfo<V, E> minPath = minEntry.getValue();
		selectedPaths.put(minVertex.value, minPath); // 放入最終的最短路徑
		paths.remove(minVertex); // 從當前的最短路徑中移除
		
		// 對離開桌面的minVertex的outEdges進行鬆弛操作
		for (Edge<V, E> edge : minVertex.outEdges) {
			// 如果edge.to已經離開桌面, 就沒有必要進行鬆弛操作
			if(selectedPaths.containsKey(edge.to.value)) continue;
			relaxForDijkstra(edge, minPath, paths);
		}
	}
	selectedPaths.remove(begin);
	return selectedPaths;
}
/**
 * 鬆弛
 * @param edge 需要進行鬆弛的邊
 * @param fromPath edge的from的最短路徑信息
 * @param paths 存放着其他(對於dijkstra來說, 就是還沒有離開桌面的點)的最短路徑信息
 */
private void relaxForDijkstra(Edge<V, E> edge, PathInfo<V, E> fromPath, Map<Vertex<V, E>, PathInfo<V, E>> paths) {
	// 新的可選擇的最短路徑: beginVertex到edge.from的最短路徑 + edge.weight
	E newWeight = weightManager.add(fromPath.weight, edge.weight);
	// 以前的最短路徑: beginVertex到edge.to的最短路徑
	PathInfo<V, E> oldPath = paths.get(edge.to);
	if(oldPath != null && weightManager.compare(newWeight, oldPath.weight) >= 0) return;
	if(oldPath == null) { // 新創建的邊
		oldPath = new PathInfo<>();
		paths.put(edge.to, oldPath);
	} else { // 以前就存在的邊
		oldPath.edgeInfos.clear();
	}
	oldPath.weight = newWeight;
	oldPath.edgeInfos.addAll(fromPath.edgeInfos);
	oldPath.edgeInfos.add(edge.info());
}

/**
 * 從paths中挑一個最小的路徑出來(遍歷)
 * 可用小頂堆進行優化
 */
private Entry<Vertex<V, E>, PathInfo<V, E>> getMinPath(Map<Vertex<V, E>, PathInfo<V, E>> paths) {
	Iterator<Entry<Vertex<V, E>, PathInfo<V, E>>> it = paths.entrySet().iterator();
	Entry<Vertex<V, E>, PathInfo<V, E>> minEntry = it.next();
	while (it.hasNext()) {
		Entry<Vertex<V, E>, PathInfo<V, E>> entry = it.next();
		if (weightManager.compare(entry.getValue().weight, minEntry.getValue().weight) < 0) {
			minEntry = entry;
		}
	}
	return minEntry;
}

dijkstra 測試

運行:
在這裏插入圖片描述
在這裏插入圖片描述

Bellman-Ford

Bellman-Ford 也屬於單源最短路徑算法支持負權邊,還能檢測出是否有負權環

  • 算法原理:對所有的邊進行 V – 1 次鬆弛操作( V 是節點數量),得到所有可能的最短路徑
  • 時間複雜度:O(EV) ,E 是邊數量,V 是節點數量

下圖的最好情況是恰好從左到右的順序對邊進行鬆弛操作:

  • 對所有邊僅需進行 1 次鬆弛操作就能計算出A到達其他所有頂點的最短路徑
    在這裏插入圖片描述

最壞情況是恰好每次都從右到左的順序對邊進行鬆弛操作:

  • 對所有邊需進行 V – 1 次鬆弛操作才能計算出A到達其他所有頂點的最短路徑
    在這裏插入圖片描述

Bellman-Ford – 實例

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

Bellman-Ford – 代碼實現

@Override
public Map<V, PathInfo<V, E>> shortestPath(V begin) {
	// return dijkstra(begin);
	return bellmanFord(begin);
}
private Map<V, PathInfo<V, E>> bellmanFord(V begin) {
	Vertex<V, E> beginVertex = vertices.get(begin); // 源點
	if(beginVertex == null) return null;
	
	// 存放當前最短路徑信息(不斷的進行鬆弛操作, 會變成最終的最短路徑)
	Map<V, PathInfo<V, E>> selectedPaths = new HashMap<>();
	// 初始化源點的最短路徑信息
	selectedPaths.put(begin, new PathInfo<>(weightManager.zero()));		

	int count = vertices.size() - 1; // 進行 V -1 次鬆弛操作, 必然能找到最短路徑
	for (int i = 0; i < count; i++) {
		for (Edge<V, E> edge : edges) { // 對所有邊進行鬆弛操作
			// 獲取該邊的始點的最短路徑信息, 用於後面進行鬆弛操作
			PathInfo<V, E> fromPath = selectedPaths.get(edge.from.value);
			// 如果該點的始點沒有最短路徑信息,鬆弛必然失敗,直接進入下一輪
			if(fromPath == null) continue;
			relax(edge, fromPath, selectedPaths); // 鬆弛操作
		}
	}
	
	// 檢測負權環, 前面已經鬆弛了V-1次,這裏如果鬆弛第 V 次仍然可以成功, 說明有負權環
	for (Edge<V, E> edge : edges) {
		PathInfo<V, E> fromPath = selectedPaths.get(edge.from.value);
		if(fromPath == null) continue;
		if(relax(edge, fromPath, selectedPaths)) {
			System.out.println("有負權環, 不存在最短路徑");
			return null;
		}
	}
	
	selectedPaths.remove(begin); // 從最短路徑中移除源點的最短路徑信息
	return selectedPaths;
}

/**
 * 鬆弛
 * @param edge 需要進行鬆弛的邊
 * @param fromPath edge的from的最短路徑信息
 * @param paths 存放着其他的最短路徑信息
 */
private boolean relax(Edge<V, E> edge, PathInfo<V, E> fromPath, Map<V, PathInfo<V, E>> paths) {
	// 新的可選擇的最短路徑: beginVertex到edge.from的最短路徑 + edge.weight
	E newWeight = weightManager.add(fromPath.weight, edge.weight);
	// 以前的最短路徑: beginVertex到edge.to的最短路徑
	PathInfo<V, E> oldPath = paths.get(edge.to.value);
	if(oldPath != null && weightManager.compare(newWeight, oldPath.weight) >= 0) return false;
	if(oldPath == null) { // 新創建的邊
		oldPath = new PathInfo<>();
		paths.put(edge.to.value, oldPath);
	} else { // 以前就存在的邊
		oldPath.edgeInfos.clear();
	}
	oldPath.weight = newWeight;
	oldPath.edgeInfos.addAll(fromPath.edgeInfos);
	oldPath.edgeInfos.add(edge.info());
	return true;
}
/**
 * 從paths中挑一個最小的路徑出來(遍歷)
 * 可用小頂堆進行優化
 */
private Entry<Vertex<V, E>, PathInfo<V, E>> getMinPath(Map<Vertex<V, E>, PathInfo<V, E>> paths) {
	Iterator<Entry<Vertex<V, E>, PathInfo<V, E>>> it = paths.entrySet().iterator();
	Entry<Vertex<V, E>, PathInfo<V, E>> minEntry = it.next();
	while (it.hasNext()) {
		Entry<Vertex<V, E>, PathInfo<V, E>> entry = it.next();
		if (weightManager.compare(entry.getValue().weight, minEntry.getValue().weight) < 0) {
			minEntry = entry;
		}
	}
	return minEntry;
}

測試

在這裏插入圖片描述
在這裏插入圖片描述

Floyd

Floyd 屬於多源最短路徑算法,能夠求出任意2個頂點之間的最短路徑支持負權邊

  • 時間複雜度:O(V3),效率比執行 V 次 Dijkstra 算法要好( V 是頂點數量)

注:單源最短路徑算法對每個頂點求一次,同樣可以求出任意2個頂點之間的最短路徑

算法原理:

  • 從任意頂點 i 到任意頂點 j 的最短路徑不外乎兩種可能
    ① 直接從 ij
    ② 從 i 經過若干個頂點到 j
  • 假設 dist(i, j) 爲頂點 i 到頂點 j 的最短路徑的距離
  • 對於每一個頂點 k,檢查 dist(i, k) + dist(k, j)dist(i, j) 是否成立
    如果成立,證明從 ik 再到 j 的路徑比 i直接到 j 的路徑短,
    設置 dist(i, j) = dist(i, k) + dist(k, j)
  • 當我們遍歷完所有結點 kdist(i, j) 中記錄的便是 ij 的最短路徑的距離

算法原理僞代碼:
在這裏插入圖片描述

Floyd – 代碼實現

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章