Java邻接表表示加权有向图,附dijkstra最短路径算法

从A到B,有多条路线,要找出最短路线,应该用哪种数据结构来存储这些数据。

 

这不是显然的考查图论的相关知识了么,

1.图的两种表示方式:

邻接矩阵:二维数组搞定

邻接表:Map<Vertext,List<Edge>>搞定

其中邻接矩阵适用于稠密图,即图上的任意两点之间均(差不多都)存在一条边。

而A到B之间的路线,显然是稀疏图,果断的选用邻接表。

2.加权有向图最短路径问题,典型的dijkstra最短路径算法。

 

说干就干,翻翻《数据结构与算法》,自己用Java大概实现了一下,具体代码如下:

 

实现思路:

1,定义一个类:有向图类:Graph。

有向图类的子类:节点类:Vertex,边类:Vertex。

节点类:保存节点名称,上一个节点,长度等属性。

边节点:保存每条边的两边的节点,通过边找到对应的另一条节点。

2,该类有两个属性:

1,List<Vertex> vertexList:保存图的顶点集合,便于遍历顶点的时候查找对应集合。
2,Map<Vertex, List<Edge>> ver_edgeList_map:图的每个顶点对应的有向边。

3,为了能够记录最短路径,需要为每个节点定义一个属性:父节点,表示父节点到该点的距离最短。

3,每个节点有多个属性:

String name;  //节点名字
boolean known; //此节点之前是否已知,如果未知的话,则需要初始化距离adjuDist和parent属性
int adjuDist; //保存从开始节点到此节点距离
Vertex parent; //当前从初始节点到此节点的最短路径下,的父节点。

4,从起点节点开始查找。

比较规则:从A节点开始比较,对其指向的B节点进行初始化和比较:

如果B节点未被初始化,先设置该B节点的父节点为A节点,距离为边长加上A节点的adjuDist。

如果已经初始化完了,则重新比较:

如果A节点加边长小于B节点的adjuDist,则证明A节点到B节点的距离最短,设置A节点为B节点父节点,并且长度修改为A节点的adjuDist加上边长。

否则不做操作。

5,等所有的节点初始化完了,从终止节点开始,通过终止节点的父节点找到上一个节点,输出节点的路径。

 

代码如下:

package 笔试题;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
 
public class Graph{
    
    private List<Vertex> vertexList;   //图的顶点集
    private Map<Vertex, List<Edge>> ver_edgeList_map;  //图的每个顶点对应的有向边
    
    public Graph(List<Vertex> vertexList, Map<Vertex, List<Edge>> ver_edgeList_map) {
        super();
        this.vertexList = vertexList;
        this.ver_edgeList_map = ver_edgeList_map;
    }
 
    public List<Vertex> getVertexList() {
        return vertexList;
    }
 
    public void setVertexList(List<Vertex> vertexList) {
        this.vertexList = vertexList;
    }
 
    
    public Map<Vertex, List<Edge>> getVer_edgeList_map() {
        return ver_edgeList_map;
    }
 
    public void setVer_edgeList_map(Map<Vertex, List<Edge>> ver_edgeList_map) {
        this.ver_edgeList_map = ver_edgeList_map;
    }
 
 
    static class Edge{
        private Vertex startVertex;  //此有向边的起始点
        private Vertex endVertex;  //此有向边的终点
        private int weight;  //此有向边的权值
        
        public Edge(Vertex startVertex, Vertex endVertex, int weight) {
            super();
            this.startVertex = startVertex;
            this.endVertex = endVertex;
            this.weight = weight;
        }
        
        public Edge()
        {}
        
        public Vertex getStartVertex() {
            return startVertex;
        }
        public void setStartVertex(Vertex startVertex) {
            this.startVertex = startVertex;
        }
        public Vertex getEndVertex() {
            return endVertex;
        }
        public void setEndVertex(Vertex endVertex) {
            this.endVertex = endVertex;
        }
        public int getWeight() {
            return weight;
        }
        public void setWeight(int weight) {
            this.weight = weight;
        }
    }
    
     static class Vertex {
        private final static int infinite_dis = Integer.MAX_VALUE;
        
        private String name;  //节点名字
        private boolean known; //此节点之前是否已知
        private int adjuDist; //此节点距离
        private Vertex parent; //当前从初始节点到此节点的最短路径下,的父节点。
        
        public Vertex()
        {
            this.known = false;
            this.adjuDist = infinite_dis;
            this.parent = null;
        }
        
        public Vertex(String name)
        {
            this.known = false;
            this.adjuDist = infinite_dis;
            this.parent = null;
            this.name = name;
        }
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public boolean isKnown() {
            return known;
        }
        public void setKnown(boolean known) {
            this.known = known;
        }
        public int getAdjuDist() {
            return adjuDist;
        }
        public void setAdjuDist(int adjuDist) {
            this.adjuDist = adjuDist;
        }
        
        public Vertex getParent() {
            return parent;
        }
 
        public void setParent(Vertex parent) {
            this.parent = parent;
        }
        
        /**
         * 重新Object父类的equals方法
         */
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Vertex)) {
                throw new ClassCastException("an object to compare with a Vertext must be Vertex");
            }
            
            if (this.name==null) {
                throw new NullPointerException("name of Vertex to be compared cannot be null");
            }
            
            return this.name.equals(obj);
        }
    }
    
    public void setRoot(Vertex v)
    {
        v.setParent(null);
        v.setAdjuDist(0);
    }
    
    
    /**
     * 
     * @param startIndex dijkstra遍历的起点节点下标
     * @param destIndex dijkstra遍历的终点节点下标
     */
    public void dijkstraTravasal(int startIndex,int destIndex)
    {
        Vertex start = vertexList.get(startIndex);
        Vertex dest = vertexList.get(destIndex);
        String path = "["+dest.getName()+"]";
        
        setRoot(start);
        updateChildren(vertexList.get(startIndex));
        
        int shortest_length = dest.getAdjuDist(); 
        
        while((dest.getParent()!=null)&&(!dest.equals(start)))
        {
            path = "["+dest.getParent().getName()+"] --> "+path;
            dest = dest.getParent();
        }
        
        System.out.println("["+vertexList.get(startIndex).getName() +"] to ["+
                vertexList.get(destIndex).getName()+"] dijkstra shortest path :: "+path);
        System.out.println("shortest length::"+shortest_length);
    }
    
    /**
     * 从初始节点开始递归更新邻接表
     * @param v
     */
    private void updateChildren(Vertex v)
    {
        if (v==null) {
            return;
        }
        
        if (ver_edgeList_map.get(v)==null||ver_edgeList_map.get(v).size()==0) {
            return;
        }
        //用来保存每个可达的节点
        List<Vertex> childrenList = new LinkedList<Graph.Vertex>();
        for(Edge e:ver_edgeList_map.get(v))
        {
            Vertex childVertex = e.getEndVertex();
            
            //如果子节点之前未知,则进行初始化,
            //把当前边的开始点默认为子节点的父节点,长度默认为边长加边的起始节点的长度,并修改该点为已经添加过,表示不用初始化
            if(!childVertex.isKnown())
            {
                childVertex.setKnown(true);
                childVertex.setAdjuDist(v.getAdjuDist()+e.getWeight());
                childVertex.setParent(v);
                childrenList.add(childVertex);
            }
            
            //此时该子节点的父节点和之前到该节点的最小长度已经知道了,则比较该边起始节点到该点的距离是否小于子节点的长度,
            //只有小于的情况下,才更新该点为该子节点父节点,并且更新长度。
            int nowDist = v.getAdjuDist()+e.getWeight();
            if(nowDist>=childVertex.getAdjuDist())
            {
                continue;
            }
            else {
                childVertex.setAdjuDist(nowDist);
                childVertex.setParent(v);
            }
        }
        
        //更新每一个子节点
        for(Vertex vc:childrenList)
        {
            updateChildren(vc);
        }
    }
    
}
 

测试代码:

package 笔试题;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
 
import 笔试题.Graph.Edge;
import 笔试题.Graph.Vertex;
 
/**
 * 测试用main方法
 * @author wuhui.wwh
 *
 */
public class TestGraph {
    public static void main(String[] args) {
        Vertex v1= new Vertex("v1");
        Vertex v2= new Vertex("v2");
        Vertex v3= new Vertex("v3");
        Vertex v4= new Vertex("v4");
        Vertex v5= new Vertex("v5");
        Vertex v6= new Vertex("v6");
        Vertex v7= new Vertex("v7");
        Vertex v8= new Vertex("v8");
        
        List<Vertex> verList = new LinkedList<Graph.Vertex>();
        verList.add(v1);
        verList.add(v2);
        verList.add(v3);
        verList.add(v4);
        verList.add(v5);
        verList.add(v6);
        verList.add(v7);
        verList.add(v8);
        
        Map<Vertex, List<Edge>> vertex_edgeList_map = new HashMap<Graph.Vertex, List<Edge>>();
        
        List<Edge> v1List = new LinkedList<Graph.Edge>();
        v1List.add(new Edge(v1,v2,6));
        v1List.add(new Edge(v1,v4,1));
        v1List.add(new Edge(v1,v4,1));
        
        List<Edge> v2List = new LinkedList<Graph.Edge>();
        v2List.add(new Edge(v2,v3,43));
        v2List.add(new Edge(v2,v4,11));
        v2List.add(new Edge(v2,v5,6));
        
        List<Edge> v3List = new LinkedList<Graph.Edge>();
        v3List.add(new Edge(v3,v8,8));
        
        List<Edge> v4List = new LinkedList<Graph.Edge>();
        v4List.add(new Edge(v4,v3,15));
        v4List.add(new Edge(v4,v5,12));
        
        List<Edge> v5List = new LinkedList<Graph.Edge>();
        v5List.add(new Edge(v5,v3,38));
        v5List.add(new Edge(v5,v8,13));
        v5List.add(new Edge(v5,v7,24));
        
        List<Edge> v6List = new LinkedList<Graph.Edge>();
        v6List.add(new Edge(v6,v5,1));
        v6List.add(new Edge(v6,v7,12));
        
        List<Edge> v7List = new LinkedList<Graph.Edge>();
        v7List.add(new Edge(v7,v8,20));
        
        vertex_edgeList_map.put(v1, v1List);
        vertex_edgeList_map.put(v2, v2List);
        vertex_edgeList_map.put(v3, v3List);
        vertex_edgeList_map.put(v4, v4List);
        vertex_edgeList_map.put(v5, v5List);
        vertex_edgeList_map.put(v6, v6List);
        vertex_edgeList_map.put(v7, v7List);
        
        
        Graph g = new Graph(verList, vertex_edgeList_map);
        g.dijkstraTravasal(0, 7);
    }
}

运行结果:

[v1] to [v8] dijkstra shortest path :: [v1] --> [v2] --> [v5] --> [v8]
shortest length::25

 

### 回答1: 我能给你一些提示,但实际实现还是要靠你了。可以先考虑将所有节点和边的信息用一个数组或链表存储起来,接着从源节点开始,每次从未访问过的节点中,选择距离源节点最近的节点,更新最短路径,然后继续向下访问,直到抵达目标节点。 ### 回答2: Dijkstra算法是一种用于解决加权有向图中寻找最短路径的经典算法。下面是使用C语言实现Dijkstra算法的一个具体示例: ```c #include <stdio.h> #include <stdbool.h> #define INF 9999 #define V 6 // 用邻接矩阵表示有向图 int graph[V][V] = { {0, 4, 0, 0, 0, 0}, {0, 0, 6, 0, 2, 0}, {0, 0, 0, 1, 0, 0}, {7, 0, 0, 0, 0, 0}, {0, 0, 4, 0, 0, 4}, {3, 0, 0, 0, 0, 0} }; // 辅助函数,找到还未包含在最短路径集合中的最小的距离值 int minDistance(int distances[], bool shortestPathSet[]) { int min = INF, minIndex; for (int v = 0; v < V; v++) { if (shortestPathSet[v] == false && distances[v] <= min) { min = distances[v]; minIndex = v; } } return minIndex; } // 打印最短路径的函数 void printSolution(int distances[], int n) { printf("节点\t距离\n"); for (int i = 0; i < V; i++) { printf("%d\t%d\n", i, distances[i]); } } // Dijkstra算法的实现 void dijkstra(int source) { int distances[V]; bool shortestPathSet[V]; for (int i = 0; i < V; i++) { distances[i] = INF; shortestPathSet[i] = false; } distances[source] = 0; // 迭代V-1次,每次找到一个最短路径并加入最短路径集合 for (int count = 0; count < V - 1; count++) { int u = minDistance(distances, shortestPathSet); shortestPathSet[u] = true; for (int v = 0; v < V; v++) { if (!shortestPathSet[v] && graph[u][v] && distances[u] + graph[u][v] < distances[v]) { distances[v] = distances[u] + graph[u][v]; } } } // 打印最短路径结果 printSolution(distances, V); } int main() { int source = 0; // 设置源节点为0 dijkstra(source); return 0; } ``` 以上的C代码实现了Dijkstra算法,用于寻找给定加权有向图中从源节点到其他节点的最短路径。该代码通过邻接矩阵表示有向图,并提供了一个示例供测试。函数`dijkstra()`实现了算法的主要逻辑,`minDistance()`函数用于找到距离集合最近的节点,`printSolution()`函数用于打印最短路径的结果。在`main()`函数中,可以设置源节点,并调用`dijkstra()`来计算最短路径。运行代码后,将输出每个节点的最短路径距离。 ### 回答3: Dijkstra算法是一种利用贪心策略来寻找加权有向图最短路径算法。下面是用C代码实现Dijkstra算法的具体步骤: 1. 定义结构体: ``` #define INFINITY 99999 #define MAX_SIZE 100 typedef struct { int weight; // 边的权重 int start; // 起点 int end; // 终点 } Edge; typedef struct { int vertex; // 顶点 int shortest; // 顶点到起点的最短距离 } Vertex; ``` 2. 实现Dijkstra算法函数: ``` void dijkstra(int graph[MAX_SIZE][MAX_SIZE], int start, int size) { Vertex vertices[MAX_SIZE]; // 所有顶点 int visited[MAX_SIZE] = {0}; // 标记已访问的顶点 int count, current, i, distance; // 初始化顶点数组 for (i = 0; i < size; i++) { vertices[i].vertex = i; vertices[i].shortest = INFINITY; } // 设置起点的最短距离为0 vertices[start].shortest = 0; // 遍历所有顶点 for (count = 0; count < size - 1; count++) { // 选择当前最短距离的顶点 current = findShortest(vertices, visited, size); // 标记当前顶点为已访问 visited[current] = 1; // 更新其他顶点的最短距离 for (i = 0; i < size; i++) { distance = graph[current][i]; if (!visited[i] && distance != 0 && vertices[current].shortest + distance < vertices[i].shortest) { vertices[i].shortest = vertices[current].shortest + distance; } } } // 输出最短路径 printf("最短路径为:\n"); for (i = 0; i < size; i++) { printf("%d -> %d: %d\n", start, i, vertices[i].shortest); } } ``` 3. 实现辅助函数:寻找当前顶点数组中最短距离的顶点 ``` int findShortest(Vertex vertices[], int visited[], int size) { int i, shortestDistance = INFINITY, shortestVertex; for (i = 0; i < size; i++) { if (!visited[i] && vertices[i].shortest < shortestDistance) { shortestDistance = vertices[i].shortest; shortestVertex = i; } } return shortestVertex; } ``` 4. 主函数中调用Dijkstra算法函数,并传入的邻接矩阵和起点: ``` int main() { int graph[MAX_SIZE][MAX_SIZE] = {{0, 4, 0, 0, 0, 0, 0, 8, 0}, {4, 0, 8, 0, 0, 0, 0, 11, 0}, {0, 8, 0, 7, 0, 4, 0, 0, 2}, {0, 0, 7, 0, 9, 14, 0, 0, 0}, {0, 0, 0, 9, 0, 10, 0, 0, 0}, {0, 0, 4, 14, 10, 0, 2, 0, 0}, {0, 0, 0, 0, 0, 2, 0, 1, 6}, {8, 11, 0, 0, 0, 0, 1, 0, 7}, {0, 0, 2, 0, 0, 0, 6, 7, 0}}; int start = 0; int size = MAX_SIZE; dijkstra(graph, start, size); return 0; } ``` 以上就是用C代码实现Dijkstra算法寻找加权有向图最短路径的具体步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值