dijkstra常规实现方式:迪杰斯特拉算法(dijkstra)https://blog.csdn.net/weixin_48898946/article/details/120990493
这里主要通过邻接表和优先队列进行优化。
目录
优化点一:
图的存储结构:(邻接表)
邻接表是数组加链表的存储方式,需要三个类实现:
- 边类: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