迪杰斯特拉算法优化(dijkstra)

dijkstra常规实现方式:迪杰斯特拉算法(dijkstra)https://blog.csdn.net/weixin_48898946/article/details/120990493

这里主要通过邻接表和优先队列进行优化。

目录

优化点一:

图的存储结构:(邻接表)

邻接表存储思路:

创建图:(以邻接表形式)

核心代码:

遍历邻接表:

java邻接表代码:

程序输出:

优化点二:

优先队列:

dijkstra应用优先队列:

思路:

核心代码:

总的优化后的dijkstra代码:

程序输出:


优化点一:

图的存储结构:(邻接表)

邻接表是数组加链表的存储方式,需要三个类实现:

  • 边类:ArcNode,有三个属性:边指向的顶点的索引,边的权值,指向ArcNode的next指针。
  • 顶点类:VerNode:有两个属性:顶点信息,指向ArcNode的next指针。
  • 图类:AdjGraph:主要属性有:VerNode[]用来存储各顶点,还有一些辅助数组。

邻接表存储思路:

通过用数组(VerNode结点)代表图中各顶点,其next指针指向该顶点的所有出度,即直接相邻的边(ArcNode结点),最后在图类中将顶点(VerNode)存储成数组形式统一保存。

创建图:(以邻接表形式)

思路:头插法:在遍历邻接矩阵时,将所有与某顶点有出度的点插入头结点的后面。先处理待插入结点的next指向头结点的next,再将头结点的next改为待插结点。

核心代码:

public void createGraph() {//构造图
	ArcNode nodeTemp;
	System.out.println("图的邻接表为:");
	for(int i = 0; i < n;i++) {
		for(int j = n - 1; j >= 0;j--) {
			if(edge[i][j] != INF) {
				nodeTemp = new ArcNode(j, edge[i][j]);
				nodeTemp.next = node[i].next;//头插法
				node[i].next = nodeTemp;
			}
		}
	}
}

遍历邻接表:

思路:通过图中的ArcNode数组找出“首结点”,再通过while循环,输出邻接点,直到为null。

public void printGraph() {//打印邻接表
	for(int i = 0; i < n;i++) {
		System.out.printf("%c -> ",vertex[i]);
		ArcNode nodeTemp = node[i].next;
		while(nodeTemp != null) {
			System.out.printf("%c -> ",vertex[nodeTemp.index]);
			nodeTemp = nodeTemp.next;
		}
		System.out.println("^");
	}
}

java邻接表代码:

import java.util.*;

public class Dijkstra {//主类(测试类)
	public static void main(String[] args) {
		final int INF = 0x3f3f3f3f;
		char[]vertex = {'A','B','C','D','E','F','G'};
		int [][]edge = new int [][] {
			{INF,5,7,INF,INF,INF,2},
			{5,INF,INF,9,INF,INF,3},
			{7,INF,INF,INF,8,INF,INF},
			{INF,9,INF,INF,INF,4,INF},
			{INF,INF,8,INF,INF,5,4},
			{INF,INF,INF,4,5,INF,6},
			{2,3,INF,INF,4,6,INF}
		};
		AdjGraph graph = new AdjGraph(edge, vertex);
		graph.createGraph();
		graph.printGraph();
	}
}

class VerNode{//顶点类
	int index;
	ArcNode next;
}

class ArcNode{//边类
	int index;
	int weight;
	ArcNode next;
	
	public ArcNode(int index, int weight) {
		this.index = index;
		this.weight = weight;
	}
}

class AdjGraph{//图类
	final int INF = 0x3f3f3f3f;
	VerNode []node;
	int [][]edge;
	char []vertex;
	int n;//顶点的数量
	
	public AdjGraph(int[][] edge, char[] vertex) {
		n = vertex.length;
		this.edge = edge;
		this.vertex = vertex;
		node = new VerNode[n];
		for(int i = 0; i < n;i++) {
			node[i] = new VerNode();
		}
	}
	
	public void createGraph() {//构造图
		ArcNode nodeTemp;
		System.out.println("图的邻接表为:");
		for(int i = 0; i < n;i++) {
			for(int j = n - 1; j >= 0;j--) {
				if(edge[i][j] != INF) {
					nodeTemp = new ArcNode(j, edge[i][j]);
					nodeTemp.next = node[i].next;
					node[i].next = nodeTemp;
				}
			}
		}
	}
	
	public void printGraph() {//打印邻接表
		for(int i = 0; i < n;i++) {
			System.out.printf("%c -> ",vertex[i]);
			ArcNode nodeTemp = node[i].next;
			while(nodeTemp != null) {
				System.out.printf("%c -> ",vertex[nodeTemp.index]);
				nodeTemp = nodeTemp.next;
			}
			System.out.println("^");
		}
	}
}

程序输出:

图的邻接表为:
A -> B -> C -> G -> ^
B -> A -> D -> G -> ^
C -> A -> E -> ^
D -> B -> F -> ^
E -> C -> F -> G -> ^
F -> D -> E -> G -> ^
G -> A -> B -> E -> F -> ^

优化点二:

优先队列:

优先队列就是制定队列中元素的优先级,优先级越大越优先出队,区别于普通队列的按先后顺序出队。优先队列按照根的大小分为大根堆和小根堆,大根堆是指元素越大越优先出队(即元素越大优先级越高),小根堆反之。

在java中创建优先队列可用 PriorityQueue<>(),默认为从小到大自然排序。

要想按指定方式进行排序,需要将比较器放入参数中,

即 PriorityQueue<>(Comparetor <> comparetor)

优先队列本质是堆的实现,所以进队和出队的时间复杂度均为O(logn)。

dijkstra应用优先队列:

思路:

通过将结点索引和距单源点的距离设置成类,每次在动态更新距离时都 new 出该类的对象加入优先队列中,在造出比较器对象并将其置于参数内后,每次出队都是按设置的优先级出队。

核心代码:

class Node{//优先队列中结点类
	int distance;
	int i;
	
	public Node(int distance, int i) {
		this.distance = distance;
		this.i = i;
	}
}

public void dijkstra(int index) {
	Comparator<Node> comparator = new Comparator<Node>() {
		@Override
		public int compare(Node o1, Node o2) {
			return o1.distance - o2.distance;
		}
	};
	PriorityQueue<Node> pq = new PriorityQueue<>(comparator);

    ...............
}

总的优化后的dijkstra代码:

import java.util.*;

public class Dijkstra {//主类(测试类)
	public static void main(String[] args) {
		final int INF = 0x3f3f3f3f;
		char[]vertex = {'A','B','C','D','E','F','G'};
		int [][]edge = new int [][] {
			{INF,5,7,INF,INF,INF,2},
			{5,INF,INF,9,INF,INF,3},
			{7,INF,INF,INF,8,INF,INF},
			{INF,9,INF,INF,INF,4,INF},
			{INF,INF,8,INF,INF,5,4},
			{INF,INF,INF,4,5,INF,6},
			{2,3,INF,INF,4,6,INF}
		};
		AdjGraph graph = new AdjGraph(edge, vertex);
		graph.createGraph();
		graph.printGraph();
		graph.dijkstra(6);
		graph.getPath(6);
	}
}

class VerNode{//顶点类
	int index;
	ArcNode next;
}

class ArcNode{//边类
	int index;
	int weight;
	ArcNode next;
	
	public ArcNode(int index, int weight) {
		this.index = index;
		this.weight = weight;
	}
}

class Node{//优先队列中结点类
	int distance;
	int i;
	
	public Node(int distance, int i) {
		this.distance = distance;
		this.i = i;
	}
}

class AdjGraph{//图类
	final int INF = 0x3f3f3f3f;
	VerNode []node;
	int [][]edge;
	char []vertex;
	int n;//顶点的数量
	boolean visited[];
	int distance[];
	int prePath[];
	
	public AdjGraph(int[][] edge, char[] vertex) {
		n = vertex.length;
		this.edge = edge;
		this.vertex = vertex;
		node = new VerNode[n];
		for(int i = 0; i < n;i++) {
			node[i] = new VerNode();
		}
		visited = new boolean[n];
		distance = new int [n];
		prePath = new int[n];
	}
	
	public void createGraph() {//构造图
		ArcNode nodeTemp;
		System.out.println("图的邻接表为:");
		for(int i = 0; i < n;i++) {
			for(int j = n - 1; j >= 0;j--) {
				if(edge[i][j] != INF) {
					nodeTemp = new ArcNode(j, edge[i][j]);
					nodeTemp.next = node[i].next;
					node[i].next = nodeTemp;
				}
			}
		}
	}
	
	public void printGraph() {//打印邻接表
		for(int i = 0; i < n;i++) {
			System.out.printf("%c -> ",vertex[i]);
			ArcNode nodeTemp = node[i].next;
			while(nodeTemp != null) {
				System.out.printf("%c -> ",vertex[nodeTemp.index]);
				nodeTemp = nodeTemp.next;
			}
			System.out.println("^");
		}
	}
	
	public void dijkstra(int index) {
		Comparator<Node> comparator = new Comparator<Node>() {
			@Override
			public int compare(Node o1, Node o2) {
				return o1.distance - o2.distance;
			}
		};
		PriorityQueue<Node> pq = new PriorityQueue<>(comparator);
		for(int i = 0; i < n;i++) {
			distance[i] = INF;
			prePath[i] = -1;
		}
		ArcNode t = node[index].next;
		while(t != null) {
			distance[t.index] = t.weight;
			prePath[t.index] = index;
			pq.offer(new Node(t.weight,t.index));
			t = t.next;
		}
		visited[index] = true;
		
		for(int i = 1; i < n;i++) {
			Node temp = pq.poll();//最短距离
			if(visited[temp.i])continue;
			int des = temp.i;
			visited[des] = true;
			
			ArcNode  p = node[des].next;
			while(p != null) {//更新距离
				int len = distance[des] + p.weight;
				if(len < INF && len < distance[p.index]) {
					distance[p.index] = len;
					prePath[p.index] = temp.i;
					pq.offer(new Node(len,p.index));
				}
				p =p.next;
			}
		}
		
		System.out.printf("\n顶点%c到其余各点的距离是:\n",vertex[index]);
		for(int i = 0; i < n;i++) {
			if(i == index)continue;
			System.out.printf("顶点%c到顶点%c的距离是%d米\n",vertex[index],vertex[i],distance[i]);
		}
		System.out.println();
	}
	
	public void getPath(int index) {
		int []temp = new int[n];
		System.out.printf("顶点%c到其余各顶点的路径为:\n",vertex[index]);
		for(int i = 0; i < n;i++) {
			Arrays.fill(temp, 0);
			int t = 0;
			if(i == index)continue;
			temp[t++] = i;
			int f = i;
			f = prePath[f];
			while(true) {
				if(f == index || f == -1)break;
				temp[t++] = f;
				f = prePath[f];
			}
			temp[t] = index;
			System.out.printf("顶点%c到顶点%c的路径为:",vertex[index],vertex[i]);
			for(int j = t;j >= 0; j--) {
				System.out.printf("%c ",vertex[temp[j]]);
			}
			System.out.println();
		}
	}
}

程序输出:

图的邻接表为:
A -> B -> C -> G -> ^
B -> A -> D -> G -> ^
C -> A -> E -> ^
D -> B -> F -> ^
E -> C -> F -> G -> ^
F -> D -> E -> G -> ^
G -> A -> B -> E -> F -> ^

顶点G到其余各点的距离是:
顶点G到顶点A的距离是2米
顶点G到顶点B的距离是3米
顶点G到顶点C的距离是9米
顶点G到顶点D的距离是10米
顶点G到顶点E的距离是4米
顶点G到顶点F的距离是6米

顶点G到其余各顶点的路径为:
顶点G到顶点A的路径为:G A 
顶点G到顶点B的路径为:G B 
顶点G到顶点C的路径为:G A C 
顶点G到顶点D的路径为:G F D 
顶点G到顶点E的路径为:G E 
顶点G到顶点F的路径为:G F 

猜你喜欢

转载自blog.csdn.net/weixin_48898946/article/details/121004513