數據結構圖的知識點


思維導圖




重要概念

圖的存儲結構

鄰接矩陣

1.其表示是唯一的,一維數組代表頂點,二維數組存儲圖中的邊或弧的信息。
2.對於含n個頂點的有向圖還是無向圖,無論邊的數目是多少,其存儲空間均為O(n^2)。
3.對於有向圖,鄰接矩陣數組第i行(或第i列)非0元素、非∞元素的個數正好是頂點i的出度(入度).
4.有向圖的鄰接矩陣要有頂點數組
5.常用於提取邊權值的算法

鄰接表

1.其表示是不唯一的。
2.適用於提取某個頂點的所有鄰接點的算法。
3.鄰接表畫法有三種分別是:無向圖、有向圖、有向帶權圖。

圖的遍歷

以鄰接表為存儲結構的DFS算法

int visited[MAX]={0};//全局數組
void DFS(AdjGraph *G,int V)
{ArcNode *p;
visited[v]=1;//置以訪問標記
printf("%d",v);
p=G->adjust[v].firstarc;//輸出被訪問頂點編號
while(!p==NULL)
{
if(visited[p->adjvex]==0)
DFS(G,p->adjvex);
p=p->nextarc//p指向下一個鄰接點
}
}

用圖遍歷求迷宮問題

用棧搜索迷宮路徑使用DFS算法,用隊列搜索迷宮路徑使用BFS算法。

最小生成樹

Prim算法(加點圖)

  • 該算法時間復雜度為 O(n^2),與圖結構的邊數無關,適合稠密圖。

算法步驟

  • 將初始化的頂點v加入U
  • 重復以下步驟(n-1)次,使其他(n-1)個頂點加入U中
  1. 將頂點最小邊選出加入TE,該邊在U-V中頂點為k,將k加入U中
    2.考察U-V中所有頂點,更新每組權值最小的邊,諾小於之前邊權值則該邊作為侯選邊

代碼實現

void MiniSpanTree_Prim(MGraph G)
{
    int min;
    int i,j,k;
    int closest[MAXV];    //存貯最小邊在 U 中的頂點
    int lowcost[MAXV];    //存儲最小邊上的權值
    
    for(i = 1; i < G.n; i++)    //初始化,給lowcost[]、closest[]置初值
    {
        lowcost[i] = G.edges[0][i];    //將 v0 頂點存在邊的權值存入 lowcost
        closest[i] = 0;    //初始化都為 v0 的下標
    }
    for(i = 1; i < G.n; i++)//找出(n-1)個頂點
    {
        min = INFINITY;    //初始化最小權值為 ∞
        j = 1;
        k = 0;
        while(j < G.n)    //循環全部頂點
        {
            if(lowcost[j] != 0 && lowcost[j] < min)///如果權值不為0,並且權值小於min
            {                
                min = lowcost[j];    //令當前權值為最小值
                k = j;    //當前最小值的下標賦給 k
            }
            j++;
        }
        cout <<closest[k] << k;    //輸出當前頂點邊中權值最小邊
        lowcost[k] = 0;    //表示該節點已添加入生成樹
        for(j = 1; j < G.n; j++)
        {
            if(lowcost[j] != 0 && G.edges[k][j] < lowcost[j])
            {         //若下標為 k 的頂點各邊權值小於當前頂點未被加入生成樹權值
                lowcost[j] = G.edges[k][j];    //將最小權值存入 lowcost
                closest[j] = k;    //將下標為 k 的頂點存入 closest
            }
        }
    }
}



Kruskal算法(加邊圖)

  • 該算法時間復雜度為O(e㏒2e),且算法執行時間僅有圖的邊數有關,更適合於稀疏圖。

算法步驟

  • 將邊集數組權值按小到大排序
  • 在邊集數組中選取邊(i,j),判斷是否產生回路,即查找頂點數組Vi,Vj所在連通分量n,m是否相等,諾不相等則不存在回路;諾相等,則選擇下一條權值最小的邊。

代碼實現

void MiniSpanTree_Kruskal(MGraph G)
{
    int i,n,m;
    Edge edges[MAXE];    //定義邊集數組
    int parent[MAXV];    //判斷回路的並查集
 
    for(i = 0; i < G.n; i++)//循環每一條邊
    {
        n = Find(parent, edges[i].begin);    
        m = Find(parent, edges[i].end);
        if(m != n)    //若 n 和 m 不相等,則說明回路不存在(這條邊的兩端點不在同一個連通)
        {
            parent[n] = m;    //將此邊結尾頂點放入下標為起點的 parent 中
            cout << edges[i].begin <<  edges[i].end << edges[i].weight << endl;
        }
    }
}
 
int Find(int *parent, int f)    //查連線頂點的尾部下標
{
    while(parent[f] > 0)
        f = parent[f];
    return f;
}

最短路徑

Dijkstra算法

  • 算法時間復雜度為O(n^2)

算法設計

1.S[]:以求出最短路徑的頂點集合
2.path[]:從v0到終點vi的最短路徑上,vi的前驅節點;若v0到vi沒有路徑則用-1作為前驅節點序號
3.D[]:是記錄v0到vi的最短路徑的長度,若v0到vi有直接路徑則其初態為弧的權值,若無則為 ∞

代碼實現

void ShortestPath_DIJ(MGraph g, int v0)
{   //初始化
    int v,min;
    int Path[MAXV],S[MAXV],D[MAXV];
    
    for(int i = 0; i < g.n; i++)
    {
        S[i] = false;    //S 初始化為空集
        D[i] = g.edges[v0][i];    //將 v0 到各個終點的弧的權值
        if(D[i] <MAXINT)    //v0 到 i 的弧存在,設置前驅為 v0
        {
            Path[i] = v0;
        }
        else    //v0 到 i 的弧不存在,設置前驅為 -1
        {
            Path[i] = -1;
        }
    }
    S[v0] = true;    // v0 加入 S
    D[v0] = 0;    //源點到源點本身的距離為 0
 
    /*初始化結束,開始最短路徑求解*/
    for(int i = 1; i < g.n; i++)    //依次考慮剩余的 n - 1 個頂點
    {
        min = MAXINT;
        for(int j = 0; j < g.n; j++)
        {
            if(S[j] == false && D[j] < min)    //若 vj 未被添加入 S 且路徑最短
            {
                v = j;    //選擇當前最短路徑,終點是 v
                min = D[j];
            }
        }
        s[v] = true;    //將 v 加入 S
        for(int j = 0; j < g.n; j++)
        {
            if(S[j] == false && (D[v] + g.edges[v][j] < D[j]))    //判斷是否要更新路徑
            {
                D[j] = D[v] + g.edges[v][j];
                Path[j] = v;    //修改 vj 的前驅為 v 
            }
        }
    }
}

Floyd算法

  • 其時間復雜度為O(n^3)

算法實現

1.圖用鄰接矩陣存儲
2.二維數組D[][]:存放頂點對之間最短路徑長度
3.path[][]:從Vi到Vj最短路徑上,Vj的前驅節點序號

代碼實現

void ShortestPath_Floyd(MGraph g)
{
    int Path[MAXV][MAXV];    //最短路徑上頂點 vj 的前驅節點的序號
    int [MAXV][MAXV];    //記錄頂點 vi 和 vj 之間的最短路徑長度
    int i,j,k;
     for(int i = 1; i < g.n; i++)
    {
        for(int j = 1; j < g.n; j++)
        {
            D[i][j] = g.edges[i][j];
            if(D[i][j] < MAXINT && i != j)
            {
                Path[i][j] = i;    //若 i 和 j 之間有弧,則將 j 的前驅置為 i;
            }
            else
            {
                Path[i][j] = -1;    //若 i 和 j 之間沒有弧,則將 j 的前驅置為 -1;
            }
        }
    }
    for(int k = 0; k < g.n; k++)//依次考察所有頂點
    {
        for(int i = 1; i < g.n; i++)
        {
            for(int j = 1; j < g.n; j++)
            {
                if(D[i][k] + D[k][j] < D[i][j])    //從 i 經過 k 到 j 的路徑更短
                {
                    D[i][j] = D[i][k] + D[k][j];    //修改路徑長度
                    Path[i][j] = Path[k][j];    //修改最短路徑
                }
            }
        }
    }
   Dispath(g,D,path)//函數輸出最短路徑
}

拓撲排序

1.從有向圖中選取一個沒有前驅的頂點並輸出他
2.從圖中刪去該頂點,及該頂點出發的邊
3.重復上面步驟直至圖中不存在沒有前驅的頂點

AOE網和關鍵路徑

1.事件的最早發生時間ve=頂點v的最早發生時間。
2.事件的最晚發生時間vl=即頂點v的最晚發生時間(每個事件最晚需要開始的時間)。
3.活動的最早開始時間e=弧ai的最早發生時間。
4.活動的最晚開始晚時間l=弧ai的最晚發生時間。

  • 關鍵活動:l(i) = e(i)的活動
  • 求關鍵活動
    *e(i) = ve(j)
    *l(i) = vl(k) - dut<j,k>//從后面頂點的vl 減去<j,k>邊的權值

疑難問題及解決方案

PTA題集


解決辦法

  • 之前少考慮了當最短路徑的長度不變時,花費減小,要更新cost[]的值

Dijkstra算法理解不透徹

  • 對於如何檢查路徑是否有回路的代碼不理解

解決辦法

查找網上內容,學會了如何通過s1和s2的計較,看他們是否相同,來判斷路徑中是否有回路,這個方法也可以用在拓撲排序中。


免責聲明!

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



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