利用鏈式前向星在無權圖中實現尋找兩點最短路徑

最近參加了中興捧月的比賽,特地挑了道圖算法的題來做。經過幾天的學習和思考,利用廣度搜索來解決“無權圖兩點最短路徑”,存儲結構上,用鏈式前向星處理“稀疏圖”能達到一個較低的時間複雜度。

那麼什麼是鏈式前向星呢? 其實它是一種鄰接表的數組實現方式,使用它,既能減少代碼的複雜度,也能在一定的條件下節省存儲空間,下面我們先學習一下這種數據存儲結構(下面的課件摘自:http://malash.me/200910/linked-forward-star/


鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯


鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯

鏈式前向星及其簡單應用 - Malash - Malash的OI生涯


鏈式前向星及其簡單應用 - Malash - Malash的OI生涯


由上可見,鏈式前向星其實模擬了指針的過程,這樣實現起來比鄰接鏈表要更爲簡單,編寫效率可以說是相當高。

利用鏈式前向星,可以快速有效地解決諸如:SPFA,圖的遍歷等等問題。


接下來下面我們可以先來看看題目:

在一個網絡拓撲中(可以支持數千個點的規模),邊是雙向的,兩點之間最多有一條邊,所有邊的距離相等(也就是權重爲1),給出源和目的兩個點,需要找出滿足條件的路徑。

1。找出源和目的之間的一條主用路徑。

2。找出源和目的之間的一條備用路徑。備用路徑和主用路徑至少有一個點或邊不相同。

關於備用路徑可能滿足下列約束:

1)和主用路徑沒有相同的中間節點。

2)和主用路徑沒有相同的邊。

例如:

示例數據

A_NE_ID,Z_NE_ID

2,28

4,48

9,45

10,1

10,11

11,12

11,2

12,13

13,14

15,23

16,24

17,25

18,19

18,1

20,21

20,29

20,3

21,22

22,23

23,24

24,32

26,27

26,34

27,28

27,35

28,36

29,37

30,31

32,40

32,33

32,31

33,25

33,41

36,44

37,38

37,45

39,30

39,4

40,5

40,49

43,42

43,50

43,6

44,43

45,7

45,8

46,37

46,47

47,48

48,40

拓撲圖描述文件

003602603.png

拓撲圖文件說明,出於簡化的目的,網絡拓撲節點用數字表示。

003605901.png


程序支持命令行參數,

/f後表示拓撲圖文件

/s 表示源節點

/d表示目的節點

/c 表示條件(取值1-2,滿足上面兩個約束條件之一)

/o 表示輸出結果文件

如cpath.exe  /ftopolink_example01.txt  /s20  /d32 /c2 /otopolink_result01.txt

表示根據拓撲圖文件topolink_example01.txt,計算節點20和節點32之間的主用和備用路徑,備用路徑要求滿足約束條件2。

輸出結果文件topolink_result01.txt內容可能的爲

main:  20, 21 ,22, 23,24, 32

backup: 20,29,37, 46,47,48, 40,32

解決思路如下:


003941353.png

首先我們來解決如何定義一個圖:


typedef struct EDGE
{
    int to;     //邊上的節點
    int next;   //下一個節點
    int weight; //權重
}EDGE;
typedef struct GRAPH
{
    int *vertex;    //頂點集
    EDGE *edge;     //邊集
    int index;      //下標值
    int vertexSize; //頂點個數
    int edgeSize;   //邊個數
    int *pre;       //前一個訪問對象
    int *visited;   //遍歷過的點
}GRAPH;

然後我們如何構建一個無權圖呢?只需要將邊插入兩個頂點中就好了(與鄰接鏈表是一樣)

void graphInsertE(GRAPH *g, int u, int v, int w)
{
    int index = g->index;
    g->edge[index].to = v;
    g->edge[index].next = g->vertex[u];
    g->edge[index].weight = w;
    g->vertex[u] = index++;
    g->index = index;
}

如何去搜尋圖中最短的路徑呢?其實就是去做一個廣度搜索,如果遇到了我們要找的節點,就停止搜索就好了

/*******************************************************
* Function Name: BFSSearch
* Purpose: 使用廣度優先找尋最短路徑
* Params :
*   @GRAPH *g 圖指針
*   @int s 源節點
*   @int d 目標節點
*******************************************************/
void BFSSearch(GRAPH *g, int s, int d)
{
    //初始化一個隊列
    QUEUE *q = initQueue(g->vertexSize);
    int qfront, temp;
    int flag = TRUE, i;
    //從源節點開始找尋
    g->visited[s] = 1;
    in(q, s);
    //直到找到目標節點或者隊列爲空才停止
    while(flag && !isQueueEmpty(*q))
    {
        out(q, &qfront);
        for(i = g->vertex[qfront];flag && i;i = g->edge[i].next)
        {
            if(g->edge[i].weight == 1)
            {
                temp = g->edge[i].to;
                if(!g->visited[temp])
                {
                    in(q, temp);
                    g->pre[temp] = qfront;
                    g->visited[temp] = 1;
                }
                if(temp == d)
                {
                    flag = FALSE;
                }
            }
        }
    }
    //釋放隊列
    destroyQueue(&q);
}


好了,大概的講解就到這裏,如果說大家想要了解更多的話,源代碼就在附件中,可以自己下載調試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章