1. 程式人生 > 實用技巧 >單源最短路徑-鄰接表無向網路

單源最短路徑-鄰接表無向網路

如題。

例圖如下:

借用之前“簡單圖”的程式碼生成圖,並進行修改和補充。

圖頂點程式碼:

class Node
    {
        public string v;
        public LinkedList<Edge> next;
        public Node(string x)
        {
            v = x;
        }
    }

圖的邊程式碼:

class Edge
    {
        public int index;
        public int edge_weight;
        
public Edge(int x,int y) { index = x; edge_weight = y; } }

新建一個節點類Node1,用於儲存結果:

class Node1
    {
        //到頂點的最短路徑。減100是防止運算的時候越界
        //因為圖中最大的邊權值也不到100
        public int path_value=int.MaxValue-100;
        //通往最短路徑的上一個節點
        public int pre_node = -1;
    }

圖類程式碼:

class MyTable
    {
        Node[] nodes;

        //構造方法2,獲得邊長為n的任意無向圖
        public MyTable(int n)
        {
            LinkedList<Edge> t;
            int j,k;
            StreamReader sr = new StreamReader(@"C:\Hc\1\ConsoleApp1\ConsoleApp1\1.txt", Encoding.Default);
            
if (n == -1) { Console.WriteLine("輸入圖中節點的個數:"); n = int.Parse(Console.ReadLine()); } nodes = new Node[n]; Console.WriteLine("輸入每個節點的名稱(每個名稱回車結束)"); for (int i = 0; i < n; i++) { nodes[i] = new Node(sr.ReadLine()); } for (int i = 0; i < n; i++) { Console.WriteLine($"輸入節點{nodes[i].v}的邊(序號表示,每個序號回車,-1結束)"); j = int.Parse(sr.ReadLine()); Console.WriteLine($"輸入該邊的路徑權值"); k = int.Parse(sr.ReadLine()); t = new(); do { t.AddLast(new Edge(j,k)); j = int.Parse(sr.ReadLine()); k = int.Parse(sr.ReadLine()); } while (j != -1); nodes[i].next = t; } } //按構造輸入的方式輸出圖,測試用。 public void testShow() { foreach (var item in nodes) { Console.Write($"節點{item.v},邊:"); foreach (var item1 in item.next) { Console.Write($"{nodes[item1.index].v},路徑長度:{item1.edge_weight}\t"); } Console.WriteLine(); } } //獲取指定節點的單源最短路徑 //思路:遍歷(用頂點陣列遍歷)所有節點,填入最短路徑資訊,直到再無變化為止。 public Node1[] get_Shortest_Path(int index) { //初始化 Node1[] b = new Node1[nodes.Length]; bool has_changed; int t; for (int i = 0; i < b.Length; i++) { b[i] = new Node1(); } b[index].path_value = 0; b[index].pre_node = index; //操作開始 do { //假設這一次遍歷沒有變化 has_changed = false; //遍歷所有頂點 for (int i = 0; i < nodes.Length; i++) { //對遍歷到的頂點,考察它所有的連線點和邊 foreach (var item in nodes[i].next) { //計算該邊到源點的最短路徑+這條邊的路徑,記為t t = b[item.index].path_value + item.edge_weight; //如果t小於原來記錄的最短路徑 if (t<b[i].path_value) { //則重新整理該頂點資訊 b[i].pre_node = item.index; b[i].path_value = t; has_changed = true; } } } } while (has_changed); //操作結束 //返回 return b; } //輸出最短路徑陣列 public void print_ShortPath(Node1[] x) { for (int i = 0; i < x.Length; i++) { Console.WriteLine($"節點:{nodes[i].v},前驅節點:{nodes[x[i].pre_node].v},路徑損耗:{x[i].path_value}"); } } }

為了節約除錯時間,新建了一個檔案“1.txt”,裡面放了初始化圖的輸入。

內容如下:

a
b
c
d
e
f
2
14
4
9
5
7
-1
-1
2
9
3
6
-1
-1
0
14
4
2
1
9
-1
-1
5
15
4
11
1
6
-1
-1
2
2
0
9
5
10
3
11
-1
-1
0
7
4
10
3
15
-1
-1

主方法呼叫:

static void Main(string[] args)
        {
            int n = 6;
            MyTable a = new(n);
            a.testShow();
            Console.WriteLine("請輸入最短路徑起點編號(整數):");
            n = int.Parse(Console.ReadLine());
            a.print_ShortPath(a.get_Shortest_Path(n));
        }

執行結果:

節點a,邊:c,路徑長度:14        e,路徑長度:9   f,路徑長度:7
節點b,邊:c,路徑長度:9 d,路徑長度:6
節點c,邊:a,路徑長度:14        e,路徑長度:2   b,路徑長度:9
節點d,邊:f,路徑長度:15        e,路徑長度:11  b,路徑長度:6
節點e,邊:c,路徑長度:2 a,路徑長度:9   f,路徑長度:10  d,路徑長度:11
節點f,邊:a,路徑長度:7 e,路徑長度:10  d,路徑長度:15
請輸入最短路徑起點編號(整數):
1
節點:a,前驅節點:e,路徑損耗:20
節點:b,前驅節點:b,路徑損耗:0
節點:c,前驅節點:b,路徑損耗:9
節點:d,前驅節點:b,路徑損耗:6
節點:e,前驅節點:c,路徑損耗:11
節點:f,前驅節點:e,路徑損耗:21