1. 程式人生 > >數據結構學習筆記(圖)

數據結構學習筆記(圖)

普裏姆算法 visit 復雜 jks 代碼 出現 creat 深度優先 只需要

                          一
                        (基本概念)
1.圖的定義:圖是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示為:G(V,E),其中,G表示一個圖,V是圖G中頂點的集合,E是圖G中邊的集合。

2.與線性表、樹的比較:
(1)線性表中我們把數據元素叫元素,樹中將數據元素叫結點,在圖中數據元素,我們則稱之為頂點。
(2)線性表中可以沒有數據元素,稱為空表。樹中可以沒有結點,叫做空樹。在圖結構中,不允許沒有頂點。
(3)線性表中,相鄰的數據元素之間具有線性關系,樹結構中,相鄰兩層的結點具有層次關系,而圖中,任意兩個頂點之間都可能有關系,頂點之間的邏輯關系用邊來表示,邊集可以是空的。

3.無向邊:若頂點Vi到Vj之間的邊沒有方向,則稱這條邊為無向邊,用無序偶對(Vi,Vj)來表示。如果圖中任意兩個頂點之間的邊都是無向邊,則稱該圖為無向圖。

有向邊:若從頂點Vi到Vj的邊有方向,則稱這條邊為有向邊,也稱為弧。用有序偶<Vi,Vj>來表示,Vj稱為弧尾,Vj稱為弧頭。如果圖中任意兩個頂點之間的邊都是有向邊,則稱該圖為有向圖。

*無向邊用小括號“()”表示,而有向邊則用尖括號“<>”表示。

4.在圖中,若不存在頂點到其自身的邊,且同一條邊不重復出現,則稱這樣的圖為簡單圖。

5.在無向圖中,如果任意兩個頂點之間都存在邊,則稱該圖為無向完全圖。含有n個頂點的無向完全圖有n*(n-1)/2條邊。

6.在有向圖中,如果任意兩個頂點之間都存在方向互為相反的兩條弧,則稱該圖為有向完全圖。含有n個頂點的有向完全圖有n*(n-1)條邊。

*對於具有n個頂點和e條邊數的圖,無向圖0≤e≤n(n-1)/2, 有向圖0≤e≤n(n-1)。

7.有很少條邊或弧的圖稱為稀疏圖,反之稱為稠密圖。

8.與圖的邊或弧相關的數叫做權。這種帶權的圖通常稱為網。

9.頂點v的度是和v相關聯的邊的數目。
邊數其實就是各定點度數和的一半。
以頂點v為頭的弧的數目稱為v的入度,以v為尾的弧的數目稱為v的出度。

10.圖中頂點與頂點之間的路徑卻是不唯一的。
路徑的長度是路徑上的邊或弧的數目。
第一個頂點到最後一個頂點相同的路徑稱為回路或環。序列中頂點不重復出現的路徑稱為簡單路徑。除了第一個頂點和最後一個頂點之外,其余頂點不重復出現的回路,稱為簡單回路或簡單環。

                          二
1.無向圖中的極大連通子圖稱為連通分量。強調:
*要是子圖;
*子圖要是連通的;
*連通子圖含有極大頂點數;
*具有極大頂點數的連通子圖包含依附於這些頂點的所有邊。

2.從Vi到Vj和從Vi到Vj都存在路徑,則稱G是強連通圖。有向圖中的極大強連通子圖稱作有向圖的強連通分量。

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

4.如果一個圖有n個頂點和小於n-1條邊,則是非連通圖,如果它多於n-1條邊,必定構成一個環。不過有n-1條邊並不一定是生成樹。

5.如果一個有向圖恰有一個頂點的入度為0,其余頂點的入度均為1,則是一棵有向樹。

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

                          三

                        (術語總結)
1.圖按照有無方向分為無向圖和有向圖。無向圖由頂點和邊構成,有向圖由頂點和弧構成。弧有弧尾和弧頭之分。

2.圖按照邊或弧的多少分稀疏圖和稠密圖。如果任意兩個頂點之間都存在邊叫完全圖,有向的叫有向完全圖。若無重復的邊或頂點到自身的邊則叫簡單圖。

3.圖中頂點之間有領接點、依附的概念。無向圖頂點的邊數叫做度,有向圖頂點分為入度和出度。

4.圖上的邊或弧上帶權則稱為網。

5.圖中頂點間存在路徑,兩頂點存在路徑則說明是連通的,如果路徑最終回到起始點則稱為環,當中不重復叫簡單路徑。若任意兩頂點都是連通的,則圖就是連通圖,有向則稱強連通圖。圖中有子圖,若子圖極大連通則就是連通分量,有向則稱強連通分量。

6.無向圖中連通且n個頂點n-1條邊叫生成樹。有向圖中一頂點入度為0,其余頂點入度為1的叫有向樹。一個有向圖由若幹棵有向樹構成生成森林。

                          四
                       (圖的存儲結構)
1、圖不可能用簡單的順序存儲結構來表示。

2.圖的五種不同的存儲結構:
(1)鄰接矩陣:圖的鄰接矩陣存儲方式是用兩個數組來表示圖。一個一維數組存儲圖中頂點信息,一個二維數組(稱為鄰接矩陣)存儲圖中的邊或弧的信息。

*對稱矩陣就是n階矩陣的元滿足aij=aji。即從矩陣的左上角到右下角的主對角線為軸,右上角的元與左下角相對應的元全都是相等的(即是無向圖)。

a.判定任意兩頂點是否有邊無邊就非常容易了。
b.要知道某個頂點的度,其實就是這個頂點Vi在鄰接矩陣中第i行(或第i列)的元素之和。
c.求頂點Vi的所有鄰接點就是矩陣中第i行元素掃描一遍,arc[i][j]為1就是鄰接點。

*判斷有向圖頂點Vi到Vj是否存在弧,只需要查找矩陣中arc[i][j]為1的頂點。

*圖的鄰接矩陣存儲結構代碼:
typedef char VertexType; /*頂點類型應由用戶定義*/
typedef int EdgeType; /*邊上的權值類型應由用戶定義*/
#define MAXVEX 100 /*最大頂點數,應由用戶定義*/
#define INFINITY 65535 /*用65535來表示正無窮*/
typedef struct
{
VertexType vexs[MAXVEX]; /*頂點表*/
EdgeType arc[MAXVEX][MAXVEX] /*鄰接矩陣,可看做邊表*/
int numVertexes, numEdges; /*圖中當前的頂點數和邊數*/
}MGraph;

*無向網圖的創建代碼:
/*建立無向網圖的鄰接矩陣表示*/
void CreateMGraph(MGraph *G)
{
int i, j, k, w;
printf("輸入頂點數和邊數:\n");
scanf("%d, %d", &G->numVertexes,&G->numEdges); /*輸入頂點數和邊數*/
for(i=0;i<G->numVertexes; i++) /*讀入頂點信息,建立頂點表*/
scanf(&G->vexs[i]);
for(i=0;i<G->numVertexes; i++)
for(j=0;j<G->numVertexes; j++)
G->arc[i][j]=INFINITY; /*鄰接矩陣初始化*/
for(k=0; k<G->numEdges; k++) /*讀入numEdges條邊,建立鄰接矩陣*/
{
print("輸入邊(Vi,Vj)上的下標、下標j和權w:\n");
scanf("%d, %d, %d", &i, &j, &w); /*輸入邊(Vi,Vj)上的權w*/
G->arc[i][j]=w;
G->arc[j][i]=G->arc[i][j]; /*因為是無向圖,矩陣對稱*/
}
}

#從代碼中也可以得到,n個頂點和e條邊的無向網圖的創建,時間復雜度為O(n+n2+e),其中對鄰接矩陣Garc的初始化耗費了O(n2)的時間。


(2)鄰接表:數組與鏈表相結合的存儲方法稱為鄰接表。
*鄰接表的處理方法是這樣:
1.圖中頂點用一個一維數組存儲,當然,頂點也可以用單鏈表來存儲,不過數組可以較容易地讀取頂點信息,更加方便。另外,對於頂點數組中,每個數據元素還需要存儲指向第一個鄰接點的指針,以便於查找該頂點的邊信息。
2.圖中每個頂點vi的所有鄰接點構成一個線性表,由於鄰接點的個數不定,所以用單鏈表存儲,無向圖稱為頂點Vi的邊表,有向圖則稱為頂點Vi作為弧尾的出邊表。

*頂點表的各個結點由data和first edge兩個域表示,data是數據域,存儲頂點的信息,firstedge是指針域,指向邊表的第一個結點,即此頂點的第一個鄰接點。邊表結點由adjvex和next兩個域組成。adjvex是鄰接點域,存儲某頂點的鄰接點的頂點表中的下標,next則存儲指向邊表中下一個結點的指針。

*對於帶權值得網圖,可以在邊表結點定義中再增加一個weight的數據域,存儲權值信息即可。

#結點定義:
typedef char VertexType; /*頂點類型應由用戶定義*/
typedef int EdgeType; /*邊上的權值應由用戶定義*/

typedef struct EdgeNode /*邊表結點*/
{
int adjvex; /*鄰接點域,存儲該頂點對應的下標*/
EdgeType weight; /*用於存儲權值,對於非網圖可以不需要*/
struct EdgeNode *next; /*鏈域,指向下一個鄰接點*/
}EdgeNode;

typedef struct VertexNode /*定義表結點*/
{
VertexType data; /*頂點域,存儲頂點信息*/
EdgeNode *firstedge; /*邊表頭指針*/
}VertexNode, AdjList[MAXVEX];

typedef struct
{
AdjList adjList;
int numVertexes, numEdges; /*圖中當前頂點數和邊數*/
}GraphAdjList;


#無向圖的鄰接表的創建:
/*建立圖的鄰接表結構*/
void CreateALGraph(GraphAdjList *G)
{
int i, j, k;
EdgeNode *e;
printf("輸入頂點數和邊數:\n");
scanf("%d, %d", &G->numVertexes, &G->numEdges); /*輸入頂點數和邊數*/
for (i=0; i<G->numVertexes; i++) /*讀入頂點信息,建立頂點表*/
{
scanf(&G->adjLIst[i].data); /*輸入頂點信息*/
G->adjLIst[i].firstedge=NULL; /*將邊表置為空表*/
}
for(k=0; k<G->numEdges; k++) /*建立邊表*/
{
printf("輸入邊(Vi, Vj)上的頂點序號:\n");
scanf("%d, %d", &i, &j); /*輸入邊(Vi,Vj)上的頂點序號*/
e=(EdgeNode*)malloc(sizeof(EdgeNode)); /*向內存申請空間*/
/*生成邊表結點*/
e->adjvex=j; /*鄰接序號為j*/
e->next=G->adjList[i].firstedge; /*將e指針指向當前頂點指向的結點*/
G->adjList[i].firstedge=e; /*將當前頂點的指針指向e*/
e=(EdgeNode*)malloc(sizeof(EdgeNode)); /*向內存申請空間*/
/*生成邊表結點*/
e-adjvex=i; /*鄰接序號為i*/
e->next=G->adjList[j].firstedge; /*將e指針指向當前頂點指向的結點*/
G->adjList[j].firstedge=e; /*將當前頂點的指針指向e*/
}
}

*時間復雜度為O(n+e)。


                         五
                       (圖的遍歷)
1.概念:從圖中某一頂點出發訪遍圖中其余頂點,且使每一個頂點僅被訪問一次,這一過程就叫做圖的遍歷。

2.需要在遍歷過程中把訪問過的頂點打上標記,以避免訪問多次而不自知。具體辦法是設置一個訪問數組visited[n],n是圖中頂點的個數,初值為0,訪問過後設置為1.

3.圖的遍歷:深度優先遍歷和廣度優先遍歷。
(1)深度優先遍歷(DFS):
深度優先遍歷其實就是一個遞歸的過程,相當於樹的前序遍歷。
從圖中某個頂點v出發,訪問此頂點,然後從v的未訪問的鄰接點出發深度優先遍歷圖,直至圖中所有和v有路徑相通的頂點都被訪問到。
對於非連通圖,只需要對它的連通分量分別進行深度優先遍歷。

**對於n個頂點e條邊的圖來說,鄰接矩陣的方式訪問需要O(n2)的時間;對於鄰接表來說,需要O(n+e)時間。
顯然對於點多邊少的稀疏圖來說,鄰接表結構使得算法在時間效率上大大提高。

(2)廣度優先遍歷(BFS):
類似樹的層序遍歷。
*廣度優先遍歷和深度優先遍歷的時間復雜度是一樣的。鄰接矩陣訪問時間為O(n2),鄰接表訪問時間為O(n+e)。

比較:深度優先遍歷更適合目標比較明確,以找到目標為主要目的的情況,而廣度優先更適合在不斷擴大遍歷範圍時找到相對最優解的情況。


                          六
                       (最小生成樹)
我們把構造連通網的最小代價生成樹稱為最小生成樹。
找連通網的最小生成樹,有兩種算法:普裏姆算法和克魯斯卡爾算法。

1、普裏姆算法:
以某頂點為起點逐步找各頂點上最小權值得邊來構建最小生成樹的。
假設N=(P,{E})是連通圖,TE是N上最小生成樹中邊的集合。算法從U={u。}(u。屬於V),TE={}開始。重復執行下述操作:在所有u屬於U,v屬於V-U的邊(u,v)屬於E中找一條代價最小的邊(u。,v。)並入集合TE,同時v。並入U,直至U=V為止。此時TE中必有n-1條邊,則T=(V,{TE})為N的最小生成樹。
時間復雜度為O(n2)。

2.克魯斯卡爾算法:
以邊為目標去構建。
假設N=(V,{E})是連通圖,則令最小生成樹的初始狀態為只有n個頂點而無邊的非連通圖T={V,{}},圖中每個頂點自成一個連通分量。在E中選擇代價最小的邊,若該邊依附的頂點落在T中不同的連通分量上,則將次變加入到T中,否則舍去此邊而選擇下一條代價最小的邊。依次類推,直至T中所有頂點都在同一連通分量上為止。
克魯斯卡爾算法的時間復雜度為O(elog2e)。

總結:對比兩個算法,克魯斯卡爾算法主要是針對邊來展開,邊數少時效率會非常高,所以對於稀疏圖有很大的優勢;而普裏姆算法對於稠密圖,即邊數非常多的情況會更好一些。

                         七
                       (最短路徑)
對於網圖來說,最短路徑,是指兩頂點之間經過的邊上權值最少的路徑,並且我們稱路徑上的第一個頂點是源點,最後一個頂點是終點。

1.迪傑斯特拉(Dijkstra)算法:
這是一個按路徑長度遞增的次序產生最短路徑的算法。
時間復雜度為O(n2)。
如果是圖中任意一個頂點到另一頂點的距離,時間復雜度為O(n3)。

代碼如下:
#define MAXVEX 9
#define INFINITY 65535
typedef int Pathmatirx[MAXVEX];
typedef int ShortPathTable[MAXVEX];
typedef int ShortPathTable[MAXVEX];

void ShortestPath_Dijkstra(MGraph G, int V0, Pathmatirx *P, ShortPathTable *D)
{
int v, w, k, min;
int final[MAXVEX];
for (v=0; v<G.numVertexes; v++)
{
final[v] = 0;
(*D)[v] = G.matirx[v0][v];
(*p)[v] = 0;
}
(*D)[v0] = 0;
final[v0] = 0;
final[v0] = 1;
for (v=1; v<G.numVertexes; v++)
{
mun = INFINITY;
for (w=0; w<G.numVertexes; w++)
{
if (!final[w] && (*D)[w]<min)
{
k=w;
min=(*D)[w];
}
}
final[k]=1;
for (w=0; w<G.numVertexes; w++)
{
if(!final[w] && (min+G.matirx[k][w]<(*D)[w]))
{
(*D)[w] = min + G.matirx[k][w];
(*p)[w]=k;
}
}
}
}

2.弗洛伊德(Floyd)算法
比較經過頂點的權值,如果經過的頂點路徑比原兩點間的路徑更短,將當前兩點間的權值設為更小的一個。

代碼如下:
typedef int Pathmatirx[MAXVEX][MAXVEX];
typedef int ShortPathTable[MAXVEX][MAXVEX];
/*Floyd算法,求網圖G中各頂點V到其余頂點w最短路徑P[v][w]及帶權長度D[v][w]*/
void ShortestPath_Floyd(MGraph G, Pathmatirx *P, ShortPathTable *D)
{
int v, w, k;
for (v=0; v<G.numVertexes; ++w); /*初始化D與P*/
{
(*D)[v][w]=G.matirx[v][w]; /*D[v][w]值即為對應點間的權值*/
(*P)[v][w]=w;
}
}
for(k=0; k<G.numVertexes; ++k)
{
for(v=0; v<G.numVertexes; ++v)
{
for(w=0; w<G.numVertexes; ++w)
{
if((*D)[v][w]>(*D)[v][k] + (*D)[k][w])
{
/*如果經過下標為k頂點路徑比原兩點間路徑更短*/
/*將當前兩點間權值設為更小的一個*/
(*D)[v][w]=(*D)[v][k]+(*D)[k][w];
(*P)[v][w]=(*P)[v][k];
}
}
}
}

時間復雜度為O(n3)。


                         八
                       (拓撲排序)
1.無環,即是圖中沒有回路的意思。

2.在一個表示工程的有向圖中,用頂點表示活動,用弧表示活動之間的優先關系,這樣的有向圖為頂點表示活動的網,我們稱為AOV網。

3。設G={V,E}是一個具有n個頂點的有向圖,V中的頂點序列V1,V2,......,Vn,滿足若從頂點Vi到Vj有一條路徑,則在頂點序列中頂點Vi必在頂點Vj之前。則我們稱這樣的頂點序列為一個拓撲序列。

4.拓撲排序,其實就是一個有向圖構造拓撲序列的過程。構造時會有兩個結果,如果此網的全部頂點都被輸出,則說明它是不存在環(回路)的AOV網;如果輸出頂點數少了,也說明這個網存在環(回路),不是AOV網。

5.拓撲排序算法:
(1)對AOV網進行拓撲排序的基本思路是:從AOV網中選擇一個入度為0的頂點輸出,然後刪去此頂點,並刪除以此頂點為尾的弧,繼續重復此步驟,直到輸出全部頂點或者AOV網中不存在入度為0的頂點為止。
(2)由於拓撲排序的過程中,需要刪除頂點,顯然用鄰接表會更加方便。

**拓撲排序的整個算發起的時間復雜度為O(n+e)。

6.在一個表示工程的帶權有向圖中,用頂點表示事件,用有向邊表示活動,用邊上的權值表示活動的持續時間,這種有向圖的邊表示活動的網,我們稱之為AOE網。我們把AOE網中沒有入邊的頂點稱為始點或源點,沒有出邊的頂點稱為終點或匯點。

7.AOV網是頂點表示活動的網,它只描述活動之間的制約關系,而AOE網是用邊表示活動的網,邊上的權值表示活動持續的時間。

8.把路徑上各個活動所持續的時間之和稱為路徑長度,從源點到匯點具有最大長度的路徑叫關鍵路徑。

數據結構學習筆記(圖)