數據結構-----圖


1.  定義、概念

  1.1 graph  

  簡單圖G=(V,E) :有窮非空頂點集合V、可空邊E

  1.2 各種圖定義

(1)無向邊:頂點a到b的邊沒有方向,則邊為無向邊edge (a,b)   同(b,a)

           無向圖:圖中任意兩個頂點之間的邊都是無向邊         undirected graphs

          

 

     上面的無向圖G=(V,{E})    V={A,B,C,D}    E={(A,B),(B,C),(C,D),(D,A),(A,C)}  

 

(2)有向邊:頂點a到b的邊有方向。有向邊也叫弧arc   <a,b> a是弧頭,b是弧尾     不能寫成<b,a>

           有向圖:圖中任意兩個頂點之間的邊都是有向邊   directed graphs

             

 

   上面的有向圖:G=(V,{E})      V={A,B,C,D}     E={<A,D>,<B,A>,<C,A>,<B,C>}

 

(3)簡單圖

    在圖中不存在頂點到自身的邊,且同一條邊不重復出現

        

 

   上面的兩個圖就不是簡單圖

 

(4)完全圖

  ①無向完全圖

    任意兩個頂點之間都存在邊

    

 

      n個頂點的無向完全圖有:(n-1)+(n-2)+...+1=n*(n-1)/2 條邊

 

  ②有向完全圖

    任意兩個頂點之間存在方向互為相反的兩條弧

              

 

     n個頂點的有向完全圖有:n*(n-1) 條邊

 

(5)稀疏圖、稠密圖

  邊或弧相對較少的是稀疏圖,相對較多的是稠密圖

 

(6)網

  邊或弧上可能有權值weight

  帶權的圖叫做 網 network

 

(7)子圖 subgraph

  圖的一部分頂點和一部分邊或弧組成子圖

     

 

 

  1.3  圖的頂點與邊的關系

(1)相鄰、度

  ①無向圖

    若G=(V,{E}),邊(a,b)屬於E ,則頂點a、b互為鄰接點,邊(a,b)依附與頂點a和b(邊與a、b相關聯)

    頂點a的度degree:和這個頂點相關聯的邊的數目,記為 TD(a)

    無向圖邊的個數是各頂點度數和的一半

  ②有向圖

    若G-(V,{E}),弧<a,b>屬於E,則頂點a鄰接到頂點b,而頂點b鄰接自頂點a,弧<a,b>與頂點a、b相關聯

    頂點a的入度indegree:以頂點a為終點的弧(弧頭)的數目  記為ID(a)

    頂點a的出度outdegree:以頂點a為起始的弧(弧尾)的數目  記為OD(a)

    頂點a的度:TD(a)=ID(a)+OD(a)

 

(2)路徑

  無向圖:從頂點a到頂點b的路徑path是一個頂點序列

  有向圖:路徑也是有向的,頂點序列要滿足原本的方向關系

 

  路徑長度:路徑上的邊或弧的數目

  如果路徑第一個頂點和最后一個頂點相同,則路徑為回路或環cycle。

  序列中頂點不重復出現的路徑叫簡單路徑。

  除了第一個頂點和最后一個頂點外,其余頂點不重復出現的回路叫簡單回路或簡單環

 

  1.4  連通圖

(1)無向圖

  無向圖G中,如果從頂點a到頂點b有路徑,則a和b是連通的,如果圖中任意兩個頂點都是連通的, 則G是連通圖 connected graph

    

 

      上圖不是連通圖,因為從A到E沒有路徑

 

  無向圖的極大連通子圖叫連通分量:①子圖②子圖連通③連通子圖有極大頂點數④有極大頂點數的連通子圖包含依附於這些頂點的所有邊

  上面圖1的極大連通圖有:如下

       

 

 

 (2)有向圖

  在有向圖G中,如果對每一對不同的頂點,如a、b,從a到b和從b到a都存在路徑,則圖G是強連通圖

  有向圖的極大強連通子圖叫有向圖的強連通分量

  

 

   這是一個強連通圖,可能它本身是另一個非強連通圖的最大強連通子圖,則這個是強連通分量

 

  

 

 

  1.5   連通圖的生成樹

(1)

  一個連通圖的生成樹是一個極小的連通子圖,它含有圖中全部的n個頂點,但只有足以構成一棵樹的n-1條邊

         

 

 

 

   圖1就不是生成樹                                                   圖2是生成樹                                                   圖3也是生成樹

 

  生成樹:則一定有n個頂點和n-1條邊   反之不一定

 

  若圖有n個頂點:邊數少於n-1,則圖不連通

                                  邊數多於n-1,則必有環

 

(2)有向圖

  若一個有向圖恰好有一個頂點入度為0(是根結點),其余頂點入度均為1(該頂點只有一個雙親),則是一棵有向樹

  

  一個有向圖的生成森林由若干有向樹組成,含圖中全部頂點,但只有足以構成若干棵不相交的有向樹的弧

    

 

   一個普通的有向圖,去掉一些弧分解成兩個有向樹如下

          

 

   B的入度為0,其余頂點入度為1          F入度為0,其余頂點入度為1

 

 

2.  存儲結構

  2.1 鄰接矩陣 adjacency matrix

  用兩個結構分別存儲頂點和邊或弧。頂點不分大小主次,可用一維數組存儲;邊或弧用二維數組存儲

  設圖G有n個頂點,則鄰接矩陣是一個n*n方陣(對稱矩陣),定義如下

      

 

(1)一個無向圖及其鄰接矩陣如下:

       

 

 

   根據矩陣可獲得信息:

    判斷任意兩頂點是否有邊

    頂點度:該頂點所在行或列的元素之和

    頂點的鄰接點:掃描該頂點所在行,arc[i][j]為1就是鄰接點

       

(2)有向圖:

       

 

   上圖,v1到v0有邊,則arc[1][0]為1,  從v0到v1沒有,則arc[0][1]為0

 

(3)網:邊帶權值的圖

        

 

   ∞表示一個計算機允許的、大於所有邊上權值的值

  一個網及其鄰接矩陣如下:

           

 

   

   

(4)鄰接矩陣基本實現

1 typedef char VertexType;       //用戶自定義頂點類型
2 typedef int EdgeType;          //邊上權值由用戶自定義
3 #define MAXVEX 100             //最大頂點數,用戶定義
4 #define INFINITY 65535         //無窮
5 typedef struct {
6     VertexType vexs[MAXVEX];  //頂點表
7     EdgeType arc[MAXVEX][MAXVEX];  //鄰接矩陣,可看作邊表
8     int numVertexes,numEdges;      //圖中當前的頂點數和邊數
9 }MGraph; 

 

  創建鄰接矩陣:

 1 void createMGraph(MGraph *G)
 2 {
 3     int i,j,k,w;
 4     cin>>G->numVertexes>>G->numEdges; //輸入頂點數和邊數
 5     //建立頂點表
 6     for(i=0;i<G->numVertexes;i++) {
 7         cin>>G->vexs[i];
 8     }
 9     //鄰接矩陣初始化
10     for(i=0;i<G->numVertexes;i++) {
11         for(j=0;j<G->numVertexes;j++) {
12             G->arc[i][j] = INFINITY;
13         }
14     }
15     //讀入 numEdges 條邊,建立鄰接矩陣
16     for(k=0;k<G->numEdges;k++) {
17         cin>>i>>j>>w;   //輸入邊(vi,vj)的上表i,下標j,權重w
18         G->arc[i][j] = w;
19         G->arc[j][i] = w;  //無向圖,矩陣對稱
20     }
21 }

  

  假設n個頂點,e條邊,則鄰接矩陣的創建復雜度 O(n+n2+e)   需要空間O(n2)

  如果圖的邊數相對頂點數較少,則會浪費很多存儲空間

 

   2.2 鄰接表 adjacency list

  頂點用一個一維數組或單鏈表存儲。每個數據元素還需要存儲指向第一個鄰接點的指針以便於尋找該頂點的邊信息。

  每個頂點的所有鄰接點構成一個線性表,用單鏈表存儲。無向圖稱為頂點a的邊表,有向圖稱為頂點a作為弧尾的出邊表。

(1)一個無向圖的鄰接表

  

 

(2)有向圖

  以頂點為弧尾(弧的起始),便於計算出度   或以頂點作為弧頭(弧的終結),便於計算入度   

      

 

               

 

(3)帶權值的網

  在邊表結點中增加 weight 數據域

          

 

 (4)簡單實現

 1 typedef char VertexType;       //用戶自定義頂點類型
 2 typedef int EdgeType;          //邊上權值由用戶自定義
 3 #define MAXVEX 100
 4 
 5 typedef struct EdgeNode {      //邊表結點
 6     int adjvex;             //鄰接點域,存儲該頂點對應的下標
 7     EdgeType weight;        //權值
 8     struct EdgeNode *next;  //鏈域,指向下一個鄰接點
 9 }EdgeNode;
10 
11 typedef struct VertexNode { //頂點表結點
12     VertexType data;        //頂點信息
13     EdgeNode * firstEdge;   //邊表頭指針
14 }VertexNode,AdjList[MAXVEX];
15 
16 typedef struct {                //鄰接表
17     AdjList adjList;            //所有頂點
18     int numVertexes,numEdges;   //頂點數、邊數
19 }GraphAdjList;

 

 1 void creatALGraph(GraphAdjList *G)
 2 {
 3     int i,j,k;
 4     EdgeNode* e = nullptr;
 5     cout<<"輸入頂點數和邊數:";
 6     cin>>G->numVertexes>>G->numEdges;  //輸入頂點數和邊數
 7     for(i=0;i<G->numVertexes;i++) {  //輸入頂點信息
 8         cin>>G->adjList[i].data;
 9         G->adjList[i].firstEdge = nullptr;
10     }
11     for(k=0;k<G->numEdges;k++) {  //建立邊表
12         cout<<"輸入邊(vi,vj)上的頂點序號:";
13         cin>>i>>j;
14         e = (EdgeNode*)malloc(sizeof(EdgeNode));  //申請一個邊結點空間
15         e->adjvex = j;   //以vi為弧的頭,即弧的開始
16         e->weight = 1;   //權值默認為1
17         e->next = G->adjList[i].firstEdge;
18         G->adjList[i].firstEdge = e;    //上面兩行,將新的被指向結點j放到i的指向的第一條邊
19         //對於有向圖,上面的就夠了 因為是vi指向vj
20         //對於無向圖,還需要下面的,j的邊表里面還要有i結點
21         e = (EdgeNode*)malloc(sizeof(EdgeNode));
22         e->adjvex = i;
23         e->weight = 1;
24         e->next = G->adjList[j].firstEdge;
25         G->adjList[j].firstEdge = e;
26     }
27 }

  時間復雜度:n個頂點,e條邊則為O(n+e)     空間復雜度:如果頂點表用動態數組存儲則只需要頂點數目的空間和邊數目的空間

 

    2.3 十字鏈表 Orthogonal list

  對於有向圖的鄰接表逆鄰接表,只能很方便的計算某個頂點的出度,要計算入度則要遍歷整個表

  十字鏈表結合鄰接表和逆鄰接表

  原來的頂點結點只有一個鏈接域,改成兩個鏈接域:firstin指向入邊表頭指針  firstout指向出邊表頭指針

               

 

   邊表結點如下:

             

 

   tailvex:弧起點(弧尾)在頂點表的下標    headvex:弧終點(弧頭)在頂點表的下標

        headlink:入邊表鏈接域,指向終點相同的下一條邊     taillink:出邊表鏈接域,指向起點相同的下一條邊

  一個有向圖及其十字鏈表:

       

 

 

            

 

   簡單實現:C++

 1 class Vertex {
 2     public:
 3         friend class Edge;
 4         Vertex(int x=0,void* p=nullptr);
 5         Edge* getFirstOutEdge() const;
 6         Edge* getFirstInEdge() const;
 7         void linkTo(Vertex*);
 8         bool linkedWith(Vertex*);
 9         void deleteVertex();
10         void setId(int);
11         void* getData();
12         ~Vertex();
13     private:
14         Edge * firstOutEdge; //指向以當前結點為起點(弧尾)的邊
15         Edge * firstInEdge;  //指向以當前結點為終點(弧頭)的邊
16         int id;
17         void* data;
18 };
19 
20 class Edge{
21     public:
22         friend class Vertex;
23         Edge();
24         Edge(Vertex* s,Vertex* d);
25         void setSrc_Dst(Vertex* s,Vertex *d);
26         void deleteEdge();
27         Edge* getNextEdge_src() const;
28         Edge* getNextEdge_dsr() const;
29         Vertex* getSrc() const;
30         Vertex* getDst() const;
31         ~Edge();
32     private:
33         Vertex* src;  //指向邊的起點,弧尾
34         Vertex* dst;  //指向邊的終點,弧頭,箭頭
35         Edge* nextEdge_src; //指向相同起點的下一條邊
36         Edge* nextEdge_dst; //指向相同終點的下一條邊
37 };

  具體實現:....再說吧 吐了

 

  

 2.4  鄰接多重表

   對於無向圖,重點在頂點則可用鄰接表,重點在邊上(對訪問過的邊做操作、刪除邊等 )用鄰接多重表

           

 

 

   上面無向圖的鄰接表如下:需要10個邊結點

         

 

 

 

   鄰接多重表:

   邊結點:

          

 

 

         ivex:邊依附的頂點在頂點表的下標   jvex:同

         ilink:依附頂點ivex的下一條邊 

              jlink:依附jvex的下一條邊

             

 

             

 

    如果要刪除(v1,v4),則只要刪除該邊結點,以及修改兩個指針(指向(v1,v4)的ilink、指向(v1,v4)的jlink)

 

  2.5 邊集數組

  兩個一維數組,一個存儲頂點信息,一個存儲邊信息(數據元素由起點下標begin、終點下標end、權組成)

  邊集數組關注邊,要對頂點操作,如計算度則需要遍歷整個邊數組

          

         

 

 

 

3.  遍歷圖

    2.1 深度優先遍歷 depth first search

  鄰接矩陣的深度優先遍歷:

 1 void DFSTraverse(MGraph G)
 2 {
 3     int i = 0;
 4     bool* visited = (bool*)malloc(G.numVertexes*sizeof(bool));
 5     for(i=0;i<G.numVertexes;++i) {
 6         visited[i] = false;
 7     }
 8     for(i=0;i<G.numVertexes;++i) {
 9         if(!visited[i]) {
10             DFS(G,i,visited);
11         }
12     }
13     free(visited);
14 }
15 
16 void DFS(MGraph G,int i,bool* visited)
17 {
18     int j = 0;
19     visited[i] = true;
20     cout<<G.vexs[i]; //對頂點進行操作
21     for(j=0;j<G.numVertexes;++j) {
22         if(G.arc[i][j] == 1 && !visited[j]) {
23             DFS(G,j,visited);
24         }
25     }
26 }

  鄰接表的深度優先遍歷:

  

 1 static void DFS(GraphAdjList GL,int i,bool* visited)
 2 {
 3     EdgeNode* p = nullptr;
 4     visited[i] = true;
 5     cout<<GL.adjList[i].data; //對頂點進行操作
 6     p = GL.adjList[i].firstEdge;
 7     while(p) {
 8         if(!visited[p->adjvex]) {
 9             DFS(GL,p->adjvex,visited);
10         }
11         p = p->next;
12     }
13 }
14 void DFSTraverse(GraphAdjList GL)
15 {
16     int i = 0;
17     bool* visited = (bool*)malloc(GL.numVertexes*sizeof(bool));
18     for(i=0;i<GL.numVertexes;++i) {
19         visited[i] = false;
20     }
21     for(i=0;i<GL.numVertexes;++i) {
22         if(!visited[i]) {
23             DFS(GL,i,visited);
24         }
25     }
26     free(visited);
27 }

 

 

  2.2 廣度優先遍歷

 

 

 

void BFSTraverse(MGraph &G)
{
    int i,j;
    queue<int> q;
    vector<bool> visited(G.numVertexes,false);
    for(i=0;i<G.numVertexes;i++) {
        if(!visited[i]) {
            cout<<G.vexs[i]<<" ";
            q.push(i);
            while(!q.empty()) {
                int k = q.front();
                q.pop();
                for(j=0;j<G.numVertexes;j++) {
                    if(G.arc[k][j]==1 && !visited[j]) {
                        visited[j] = true;
                        cout<<G.vexs[j]<<" ";
                        q.push(j);
                    }
                }
            }
        }
    }
}

 

 

 

4. 最短路徑

  網圖兩頂點之間經過的邊上權值之和最小的路徑

              

 

  4.1 Dijkstra算法

  按路徑長度遞增的次序產生最短路徑。采用 貪心算法 思想,對有向/無向帶權圖尋找最短路徑。

  對不含負權的網,是最快的 單源最短路徑算法 O(n2)  

  步驟:

    由於是單源路徑,我以vo為起始點

    用一個數組distance存儲從v0到其它各點的最短路徑值,用數組parent存儲從v0到vi中間要經過的點的下標(規定v0在頂點表下標為0,vi的下標為i)

    先將v0到各點的直線距離存儲進distance(沒有直接連線則距離為無窮值 這里用65535表示INFINITY)

    首先,找到v0到其它點的直線距離中最短的路徑(第一次執行行15到行24),記下這個點是vj。后面循環再次執行着幾行,就不是找v0到其它點的最短直線距離,而是最短距離

    行25到行30是關鍵,對未訪問過的點(used里面沒標記為1的),如果v0到這個點vn的直線距離(第一次循環里面是直線距離)比從v0到vj加上從vj到vn的和還要大,則從v0到vn的最短路徑就是先從v0到vj,再從vj到vn,而不是直接從v0到vn了,這時更新一下distance[n],並且從v0到vn中間經過vj,所以在parent[n]里存下這個中間點下標j。

 

    后面的循環:行14的第二次及以后的循環

    也是先找v0到其它點的最短距離,這里就不是直線距離了。注意行18的條件used[m]==0

 1 void dijkstra(MGraph &G)
 2 {
 3     int i,j,k,m,n;
 4     int mini;
 5     int* used = (int*)malloc(G.numVertexes*sizeof(int));
 6     int* distance=(int*)malloc(G.numVertexes*sizeof(int)); //存儲起始頂點到各點的最短路徑
 7     int* parent=(int*)malloc(G.numVertexes*sizeof(int));
 8     for(i=0;i<G.numVertexes;i++) {
 9         used[i] = 0;
10         parent[i] = 0;
11         distance[i] = G.arc[0][i];   //矩陣第0行的數據
12     }
13     used[0] = 1;
14     for(i=0;i<G.numVertexes-1;i++) {
15         j=0;
16         mini = INFINITY;
17         for(m=0;m<G.numVertexes;m++) {
18             if(used[m]==0 && distance[m]<mini) {
19                 mini = distance[m];
20                 j=m;
21             }
22         }
23         //mini為行0的最小值,j為頂點下標 (vo,vj)==mini
24         used[j] = 1;
25         for(n=0;n<G.numVertexes;n++) {
26             if(used[n]==0 && (distance[n]>distance[j]+G.arc[j][n])) {
27                 distance[n] = distance[j]+G.arc[j][n];
28                 parent[n] = j;
29             }
30         }
31 
32         //輸出每次循環后distance里面的內容
33 //        for(k=0;k<G.numVertexes;++k) {
34 //            cout<<distance[k]<<"  ";
35 //        }
36 //        cout<<"\n";
37     }
38     //最短路徑算法執行完畢,distance里面存儲從第一個結點v0到其它各結點的最短路徑值
39     //parent里面存儲路線
40     for(k=0;k<G.numVertexes;++k) {
41         cout<<distance[k]<<"  ";
42     }
43     cout<<"\n";
44     for(k=0;k<G.numVertexes;++k) {
45         cout<<parent[k]<<"  ";
46     }
47     cout<<"\n";
48     free(used);
49     free(parent);
50     free(distance);
51 }

 

  畫一個簡單的網:

      

 

   需要3次進到行14的循環里面去

  distance=[x,20,5,100]

  第一次:

    行15到24:找到最短的路徑值是從v0到v2   mini=5  j=2  used[2]=1

    行25到30:

      v0到v1:v0到v1距離為20 > (v0,v2)+(v2,v1) 不成立

      v0到v3:v0到v3距離100 > (v0,v2)+(v2,v3) ,100>5+40 成立,所以distance[3]==45  parent[3]=2

    distance=[x,20,5,45]

  第二次:

    行15到24:找到最短的是從v0到v1(v0到v2的這條已經計算過了,used[2]被標記了)  mini=20  j=1  used[1]=1

    行25到30:

      v0到v3:45>(v0,v1)+(v1,v3) ,即45>20+1 成立,說明從v0到v3中間經過v1比中間經過v2距離要短些,更新distance[3]=21 parent[3]=1

    distance=[x,20,5,21]

  第三次:

    used[0]、used[1]、used[2]都被標記了,只剩下v3

    mini=21  j=3 used[3]=1

    (v0,v3)>(v0,v3)+(v3,v3)不成立

    distance=[x,20,5,21]

    parent=[0,0,0,1]

  完成最短路徑計算

   dijkstra算法只適用計算單源最短路徑,即從v0到其它任意點的最短路徑,復雜度為O(n2)。如果要計算任意兩點間最短距離,則要把每個頂點當作源點運行一次這個算法,復雜度變成了O(n3)。

  

   4.2 Floyd算法

  利用動態規划的思想

  可處理帶負權的多源最短路徑問題,不能處理有負權回路的圖。

  時間復雜度為O(n3)  空間復雜度為O(n2)

 1 void floyd(MGraph &G)
 2 {
 3     int **dis = getMNMatrix<int>(G.numVertexes,G.numVertexes);
 4     int **pa = getMNMatrix<int>(G.numVertexes,G.numVertexes);
 5     int v,w,k;
 6     //初始化兩個矩陣 distance為鄰接矩陣,path[i][j]==j
 7     for(v=0;v<G.numVertexes;++v) {
 8         for(w=0;w<G.numVertexes;++w) {
 9             dis[v][w] = G.arc[v][w];
10             pa[v][w] = w;
11         }
12     }
13     for(k=0;k<G.numVertexes;++k) {
14         for(v=0;v<G.numVertexes;++v) {
15             for(w=0;w<G.numVertexes;++w) {
16                 if(dis[v][w] > dis[v][k]+dis[k][w]) {
17                     dis[v][w] = dis[v][k]+dis[k][w];
18                     pa[v][w] = pa[v][k];
19                 }
20             }
21         }
22     }
23     print_matrix<int>(dis,G.numVertexes,G.numVertexes);
24     print_matrix<int>(pa,G.numVertexes,G.numVertexes);
25     //輸出各點到其它點的最短路徑以及中間經過的點
26     printPath(pa,dis,G);
27     freeMatrix(dis,G.numVertexes,G.numVertexes);
28     freeMatrix(pa,G.numVertexes,G.numVertexes);
29 }

  行13到22為floyd算法的關鍵部分

  其它調用的函數:

    打印路徑詳細信息:

 1 static void printIJPath(int **pa,MGraph &G,int i,int j)
 2 {
 3     cout<<G.vexs[i]<<" ";
 4     while(i != j) {
 5         cout<<G.vexs[pa[i][j]]<<" ";
 6         i = pa[i][j];
 7     }
 8 }
 9 static void printPath(int **pa,int **dis,MGraph &G)
10 {
11     for(int i=0;i<G.numVertexes;++i) {
12         for(int j=0;j<G.numVertexes;++j) {
13             if(i==j) {
14                 continue;
15             }
16             cout<<""<<G.vexs[i]<<""<<G.vexs[j]<<"的路徑為:";
17             printIJPath(pa,G,i,j);
18             cout<<"長度為:"<<dis[i][j]<<endl;
19         }
20     }
21 }

 

    獲取m*n矩陣:

 1 template <typename T>
 2 T** getMNMatrix(int m,int n)
 3 {
 4     if(m<1 || n<1) {
 5         return nullptr;
 6     }
 7     T** pary = nullptr;
 8     pary = (T**)malloc(m*sizeof(T*));
 9     if(pary == nullptr) {
10         return nullptr;
11     }
12     for(int i=0;i<m;++i) {
13         pary[i] = (T*)malloc(n*sizeof(T));
14         if(pary[i] == nullptr) {
15             freePartMatrix(pary,i);
16             pary = nullptr;
17             return nullptr;
18         }
19         memset(pary[i],0,n*sizeof(T));
20     }
21     return pary;
22 }
 1 template <typename T>
 2 /*
 3 功能:malloc申請空間部分成功,部分失敗,則回收成功的部分
 4 */
 5 static void freePartMatrix(T** pary,int i)
 6 {
 7     for(int j=i-1;j>=0;j--) {
 8         if(pary[j] != nullptr) {
 9             free(pary[j]);
10             pary[j] = nullptr;
11         }
12     }
13     free(pary);
14 }
 1 /*
 2 功能:回收整個二維數組
 3 */
 4 template <typename T>
 5 void freeMatrix(T** arr,int m,int n)
 6 {
 7     if(arr == nullptr) {
 8         return;
 9     }
10     for(int i=m-1;i>=0;--i) {
11         if(arr[i] != nullptr) {
12             free(arr[i]);
13             arr[i] = nullptr;
14         }
15     }
16     if(arr != nullptr) {
17         free(arr);
18     }
19 }
 1 template <typename T>
 2 void print_matrix(T** ary,int m,int n)
 3 {
 4     if(ary == nullptr) {
 5         cout<<"Empty Matrix"<<endl;
 6     }
 7     for(int i=0;i<m;++i){
 8         for(int j=0;j<n;++j) {
 9             cout<<ary[i][j]<<"  ";
10         }
11         cout<<"\n";
12     }
13 }

   對4.1的圖調用floyd算法:

    最短路徑值矩陣dis、路徑矩陣pa如下:

 

 

                   

 

     打印路徑信息:

              

 

 

 

 

5. 最小生成樹

   一個連通圖的生成樹:一個極小連通子圖,含圖中全部頂點,但只有足以構成一棵樹的n-1條邊

  構造連通網的最小代價生成樹是最小生成樹

  

  一個簡單無向網及其鄰接矩陣:

 

  5.1 prim算法

  以v0為起點

 1 void miniSpanTree_prim(MGraph &G)
 2 {
 3     int min,i,j,k;
 4     int adjvex[MAXVEX];  //保存相關頂點下標
 5     int lowcost[MAXVEX]; //保存相關頂點間邊的權值
 6     lowcost[0] = 0;      //初始化第一個權值為0,即v0加入生成樹
 7     adjvex[0] = 0;       //初始化第一個頂點下標為0
 8     for(i=1;i<G.numVertexes;++i) {    //讀入矩陣第一行的數據
 9         lowcost[i] = G.arc[0][i];     //將與v0頂點有關的邊的權存入數組
10         adjvex[i] = 0;
11     }
12     for(i=0;i<G.numVertexes;++i) {
13         min = INFINITY;       //初始化最小權值為無窮
14         j = 1; k = 0;
15         while(j<G.numVertexes) {
16             if(lowcost[j]!=0 && lowcost[j]<min) {
17                 min = lowcost[j];
18                 k = j;
19             }
20             ++j;
21         }
22         cout<<adjvex[k]<<" "<<k;   //輸出當前頂點邊中權值最小的邊,即(v0,vk)==min==lowcost[k]
23         lowcost[k] = 0;     //當前頂點的權值設為0,表示此頂點已經完成任務
24         for(j=1;j<G.numVertexes;++j) {
25             if(lowcost[j]!=0 && G.arc[k][j]<lowcost[j]) {
26                 lowcost[j] = G.arc[k][j];
27                 adjvex[j] = k;
28             }
29         }
30     }
31 }

  這個和dijsktra算法很像,只不過行25,不是用距離和比較,因為最小生成樹不需要計算從一個頂點到另一個頂點的距離,只要距離最小就可以了

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM