常用的单源最短路径算法一共有两个,一个是Dijkstra算法 ,一个是Bellman-ford 算法
Dijkstra 算法 不能处理含有负权边的图,Bellmanford 能够处理含负权边或包含负权回路的图。
首先是Dijkstra算法:
算法的具体思想就不多写了,算法导论上有很详细的介绍,我主要还是贴出一个代码实现。
Dijstra里面需要用到优先级队列这里笔者也给出了一个。
使用堆实现的优先级队列,Dijkstar的复杂度在VlogV。
优先队列:
package graphic;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class PriorityQueue<E> {
private final static int ROOT_INDEX =0;
private List<E> heap ;
protected Comparator<E> comparator;
public PriorityQueue() {
heap = new ArrayList<E>();
}
public PriorityQueue(Comparator<E> comparator){
this.comparator = comparator;
heap = new ArrayList<E>();
}
public void insert(E obj){
heap.add(obj);
int index = heap.size()-1;
adjustHeapUP(index);
}
public void decreaseKey(E obj){
for(int i =0;i<heap.size();i++){
E cur = heap.get(i);
if(cur.equals(obj)){
adjustHeapUP(i);
break;
}
}
}
public E extractMin(){
if(heap.isEmpty()){throw new RuntimeException();}
int index = heap.size()-1;
E min = heap.get(ROOT_INDEX);
E last = heap.get(index);
heap.set(ROOT_INDEX, last);
heap.remove(index);
adjustHeapDown(ROOT_INDEX);
return min;
}
protected void adjustHeapDown(int index){
int p = index;
int target = 2*p+1;
if(target >= heap.size())return;
if(target+1 < heap.size() && compare(heap.get(target),heap.get(target+1)) >0 ){
target ++;
}
E paret = heap.get(p);
E t = heap.get(target);
if(compare(paret, t) > 0){
heap.set(p, t);
heap.set(target, paret);
adjustHeapDown(target);
}
}
protected int compare(E src,E des){
if(comparator==null){
Comparable<E> s1 = (Comparable<E>)src;
Comparable<E> s2 = (Comparable<E>)des;
return s1.compareTo((E) s2);
}else{
return comparator.compare(src, des);
}
}
protected void adjustHeapUP(int index){
int parent = parent(index);
E p = heap.get(parent);
E cur = heap.get(index);
if(compare(cur,p)<0){
heap.set(index, p);
heap.set(parent, cur);
adjustHeapUP(parent);
}
}
public void print(){
for(E e:heap){
System.out.println(e);
}
System.out.println();
}
protected int parent(int index){
return (index-1)/2;
}
public boolean isEmpty(){
return heap.isEmpty();
}
}
Dijkstra算法:
package graphic;
import java.util.List;
public class Dijkstra {
static int NODE_NUM =0 ;
GNode[] nodes ;
PriorityQueue<GNode> queue = new PriorityQueue<GNode>();
public void addNode(int[] ids){
NODE_NUM = ids.length;
nodes = new GNode[NODE_NUM+1];
for(Integer id:ids){
nodes[id] = new GNode(id);
}
}
public void addDirectionEdge(int src,int des,int weight){
GEdge edge = new GEdge(nodes[src],nodes[des],weight);
nodes[src].edges.add(edge);
}
public void addUnDirectedEdge(int src,int des,int weight){
addDirectionEdge(src,des,weight);
addDirectionEdge(des,src,weight);
}
public void shortPath(int src,int des){
nodes[src].dis=0;
for(int i=1;i<nodes.length;i++){
queue.insert(nodes[i]);
}
while(!queue.isEmpty()){
GNode min = queue.extractMin();
if(min.nodeId == des){
printShortPath(min);
return;
}
List<GEdge> edges = min.edges;
for(GEdge edge:edges){
GNode ed = edge.des;
if(min.dis+edge.weight < ed.dis){
ed.dis = min.dis+edge.weight;
queue.decreaseKey(ed);
ed.parent = min;
}
}
}
}
private void printShortPath(GNode node){
if(node.parent!=null){
printShortPath(node.parent);
System.out.println(node.nodeId);
}
}
public static void main(String[] args) {
Dijkstra dij = new Dijkstra();
dij.addNode(new int[]{1,2,3,4,5});
dij.addUnDirectedEdge(1, 2, 1);
dij.addUnDirectedEdge(1, 3, 1);
dij.addUnDirectedEdge(2, 4, 2);
dij.addUnDirectedEdge(3, 4, 3);
dij.addUnDirectedEdge(4, 5, 3);
dij.shortPath(1, 5);
}
}
BellFord-man 算法:
对边进行迭代的算法,需要迭代|V|-1 次
复杂度为 O(EV)
package graphic;
import java.util.ArrayList;
import java.util.List;
public class BellmanFord {
static int NODE_NUM =0 ;
GNode[] nodes ;
List<GEdge> edges = new ArrayList<GEdge>();
public void addNode(int[] ids){
NODE_NUM = ids.length;
nodes = new GNode[NODE_NUM+1];
for(Integer id:ids){
nodes[id] = new GNode(id);
}
}
public void addEdge(int src,int des,int weight){
GEdge edge = new GEdge(nodes[src],nodes[des],weight);
edges.add(edge);
}
public boolean shortestPath(int src){
nodes[src].dis = 0;
for(int i=1;i<NODE_NUM;i++){
for(GEdge edge:edges){
GNode start = edge.src;
GNode end = edge.des;
if(start.dis+edge.weight < end.dis){
end.dis = start.dis+edge.weight;
end.parent = start;
}
}
}
for(GEdge edge:edges){
GNode start = edge.src;
GNode end = edge.des;
if(start.dis+edge.weight < end.dis){
return false;
}
}
return true;
}
public static void main(String[] args) {
BellmanFord dij = new BellmanFord();
dij.addNode(new int[]{1,2,3,4,5});
dij.addEdge(1, 2, 1);
dij.addEdge(1, 3, 1);
dij.addEdge(2, 4, 2);
dij.addEdge(3, 4, 3);
dij.addEdge(4, 5, 3);
dij.shortestPath(1);
for(int i=1;i<=NODE_NUM;i++){
System.out.println(dij.nodes[i].dis);
}
}
}
边和点的数据结构:
package graphic;
public class GEdge {
GNode src;
GNode des;
int weight = 0 ;
public GEdge(GNode src,GNode des,int weight) {
this.src = src;
this.des = des;
this.weight = weight;
}
}
package graphic;
import java.util.ArrayList;
import java.util.List;
public class GNode implements Comparable<GNode>{
int nodeId;
GNode parent = null;
int dis = Integer.MAX_VALUE;
int discoverTime =0;
int finishTime =0;
Color color = Color.White;
List<GEdge> edges = new ArrayList<GEdge>();
public GNode(Integer id) {
this.nodeId = id;
}
public int getNodeId() {
return nodeId;
}
public void setNodeId(int nodeId) {
this.nodeId = nodeId;
}
public GNode getParent() {
return parent;
}
public void setParent(GNode parent) {
this.parent = parent;
}
public int getDis() {
return dis;
}
public void setDis(int dis) {
this.dis = dis;
}
public int getDiscoverTime() {
return discoverTime;
}
public void setDiscoverTime(int discoverTime) {
this.discoverTime = discoverTime;
}
public int getFinishTime() {
return finishTime;
}
public void setFinishTime(int finishTime) {
this.finishTime = finishTime;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public List<GEdge> getEdges() {
return edges;
}
public void setEdges(List<GEdge> edges) {
this.edges = edges;
}
@Override
public int compareTo(GNode arg0) {
if(this.dis > arg0.dis){
return 1;
}else if(this.dis == arg0.dis)return 0;
return -1;
}
@Override
public boolean equals(Object obj) {
return this.nodeId == ((GNode)obj).nodeId;
}
@Override
public String toString() {
return "id: "+nodeId+"\tdis: "+dis;
}
}