




版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
數(shù)據(jù)結(jié)構(gòu)C++實(shí)現(xiàn)第七章圖繆淮扣顧訓(xùn)穰沈俊編著科學(xué)出版社
新世紀(jì)計(jì)算機(jī)專(zhuān)業(yè)系列教材
圖(Graph)是一種較樹(shù)更為復(fù)雜的非線(xiàn)性數(shù)據(jù)結(jié)構(gòu)。
在樹(shù)形結(jié)構(gòu)中,數(shù)據(jù)元素之間的關(guān)系是層次型的,樹(shù)中除葉子以外的每一個(gè)數(shù)據(jù)元素可以和它下一層的多個(gè)數(shù)據(jù)元素存在關(guān)系;但除根元素以外的每一個(gè)數(shù)據(jù)元素只能且必須和它上一層中的一個(gè)數(shù)據(jù)元素存在關(guān)系。而在圖形結(jié)構(gòu)中,數(shù)據(jù)元素之間的關(guān)系是任意的,圖中每一個(gè)數(shù)據(jù)元素可以和任何其它數(shù)據(jù)元素相關(guān)聯(lián)。
本章主要介紹圖的存儲(chǔ)結(jié)構(gòu)以及圖的基本操作的實(shí)現(xiàn);并利用圖論的知識(shí)討論圖的應(yīng)用的實(shí)現(xiàn)。圖的基本概念
圖是由數(shù)據(jù)元素的集合及數(shù)據(jù)元素間的關(guān)系集合組成的一種數(shù)據(jù)結(jié)構(gòu):Graph=(V,E),其中:V={x|x
某個(gè)數(shù)據(jù)對(duì)象}是數(shù)據(jù)元素的集合,在圖中,數(shù)據(jù)元素一般被稱(chēng)為頂點(diǎn)(vertex);E={(v、w)|v,wV}或E={<v、w>|v,wV&&Path(v、w)}是數(shù)據(jù)元素之間關(guān)系的集合,也叫做邊(edge)集合;Path(v、w)表示從頂點(diǎn)v到頂點(diǎn)w的一條單向通路,它是有方向的。
在圖中如果頂點(diǎn)對(duì)(v、w)是無(wú)序的,則稱(chēng)此圖為無(wú)向圖(undirectedgraph),頂點(diǎn)對(duì)(v、w)稱(chēng)為與頂點(diǎn)v和頂點(diǎn)w相關(guān)聯(lián)的一條邊。由于這條邊沒(méi)有方向,所以(v、w)與(w、v)是同一條邊;在圖中如果頂點(diǎn)對(duì)<v、w>是有序的,則稱(chēng)此圖為有向圖(directedgraph),頂點(diǎn)對(duì)<v、w>稱(chēng)為從頂點(diǎn)v到頂點(diǎn)w的一條有向邊(又稱(chēng)為弧),其中v稱(chēng)為有向邊<v、w>的始點(diǎn)(弧尾);w稱(chēng)為有向邊<v、w>的終點(diǎn)(弧頭)。顯然<v、w>與<w、v>是兩條不同的弧。
一般表示無(wú)向圖中邊的頂點(diǎn)對(duì)用一對(duì)圓括號(hào)括起來(lái);表示有向圖中弧的頂點(diǎn)對(duì)用一對(duì)尖括號(hào)括起來(lái)。
右圖所示的4個(gè)圖,其中G1和G2是無(wú)向圖,G3和G4是有向圖。
在圖G3和G4中,為了表示有向邊,邊的方向用箭頭畫(huà)出,箭頭從有向邊的始點(diǎn)指向有向邊的終點(diǎn);在無(wú)向圖中用線(xiàn)段表示邊。在下面對(duì)圖的討論中,對(duì)圖作一些限制:第一、圖中不能有從頂點(diǎn)自身到自身的邊(即自身環(huán)),就是說(shuō)不應(yīng)有形如(x,x)或<x,x>的邊。如圖(a)所示的帶自身環(huán)的圖不討論。第二、兩個(gè)頂點(diǎn)v和w之間相關(guān)聯(lián)的邊不能多于一條。如圖(b)所示的多重圖也不討論。圖的術(shù)語(yǔ)
1.完全圖(completegraph):在有n個(gè)頂點(diǎn)的無(wú)向圖中,若有n(n-1)/2條邊,則稱(chēng)此無(wú)向圖為完全無(wú)向圖。在有n個(gè)頂點(diǎn)的有向圖中,若有n(n-1)條邊,則稱(chēng)此圖為完全有向圖。在完全圖中邊(弧)數(shù)目達(dá)到最大。2.權(quán)(weight):在某些圖的應(yīng)用中,邊(?。┥暇哂信c它相關(guān)的系數(shù),稱(chēng)之為權(quán)。這些權(quán)可以表示從一個(gè)頂點(diǎn)到另一個(gè)頂點(diǎn)的距離、花費(fèi)的代價(jià)、所需的時(shí)間、次數(shù)等。這種帶權(quán)圖也被稱(chēng)為網(wǎng)絡(luò)(network)。3.鄰接點(diǎn)(adjacentvertex):如果(v,w)是無(wú)向圖G中的一條邊,則稱(chēng)v與w互為鄰接頂點(diǎn),且邊(v,w)稱(chēng)為依附于頂點(diǎn)v和w。如果<v、w>是有向圖G中的一條弧,則稱(chēng)頂點(diǎn)v鄰接到頂點(diǎn)w(也稱(chēng)v是w的前驅(qū)),頂點(diǎn)w鄰接自頂點(diǎn)v(也稱(chēng)w是v的后繼),弧<v,w>與頂點(diǎn)v與w相關(guān)聯(lián)。4.子圖(subgraph):設(shè)有兩個(gè)圖G=(V,E)和G’=(V’,E’)。若V’V且E’E,則稱(chēng)圖G’是圖G的子圖。下圖(a)和(b)給出無(wú)向圖G1的兩個(gè)子圖,圖(c)和(d)給出有向圖G3的兩個(gè)子圖。5.頂點(diǎn)的度(degree):在無(wú)向圖中,一個(gè)頂點(diǎn)v的度是依附于頂點(diǎn)v的邊的條數(shù),記作TD(v)。在有向圖中,以頂點(diǎn)v為始點(diǎn)的有向邊的條數(shù)稱(chēng)為頂點(diǎn)v的出度,記作OD(v);以頂點(diǎn)v為終點(diǎn)的有向邊的條數(shù)稱(chēng)為頂點(diǎn)v的入度,記作ID(v)。有向圖中頂點(diǎn)v的度等于該頂點(diǎn)的入度與出度之和:TD(v)=ID(v)十OD(v)。6.路徑(path):在圖G=(V,E)中,若從頂點(diǎn)vi出發(fā),沿一些邊(或?。┙?jīng)過(guò)一些頂點(diǎn)vp1、vp2、…、vpk,到達(dá)頂點(diǎn)vj,則頂點(diǎn)序列(vi、vp1、vp2、…、vpk、vj)被稱(chēng)為從頂點(diǎn)vi到頂點(diǎn)vj的路徑。7.路徑長(zhǎng)度(pathlength):對(duì)于不帶權(quán)的圖,路徑長(zhǎng)度是指此路徑上邊的數(shù)目。對(duì)于帶權(quán)圖,路徑長(zhǎng)度是指路徑上各邊的權(quán)之和。8.簡(jiǎn)單路徑與回路(cycle):對(duì)于一路徑(v1、v2、…、vm),若路徑上各頂點(diǎn)均不相同,則稱(chēng)這路徑為簡(jiǎn)單路徑。若路徑上第一個(gè)頂點(diǎn)v1和最后一個(gè)頂點(diǎn)vm相同,則稱(chēng)這樣的路徑為回路或環(huán)。9.連通圖與連通分量:在無(wú)向圖中,若從頂點(diǎn)vi到頂點(diǎn)vj有路徑,則稱(chēng)頂點(diǎn)vi與vj是連通的。如果無(wú)向圖中任意兩個(gè)頂點(diǎn)都是連通的,則稱(chēng)此無(wú)向圖是連通圖。非連通圖的極大連通子圖(包括所有連通的頂點(diǎn)和這些頂點(diǎn)依附的所有的邊)叫做連通分量。如右圖(a)所示是一個(gè)非連通圖,圖(b)所示是相應(yīng)的三個(gè)連通分量。10.強(qiáng)連通圖與強(qiáng)連通分量(stronglyconnecteddigraph):在有向圖中,若對(duì)于頂點(diǎn)vi和vj,存在一條從vi到vj和從vj到vi的路徑,則稱(chēng)頂點(diǎn)vi和頂點(diǎn)vj是強(qiáng)連通。如果有向圖中任意兩個(gè)頂點(diǎn)都是強(qiáng)連通的,則稱(chēng)此有向圖為強(qiáng)連通圖。非強(qiáng)連通圖的極大強(qiáng)連通子圖叫做強(qiáng)連通分量。11.生成樹(shù)(spanningtree):一個(gè)連通圖的生成樹(shù)是它的極小連通子圖,它包含圖中全部n個(gè)頂點(diǎn)和僅使這n個(gè)頂點(diǎn)連通的n-1條邊。如果一個(gè)有向圖只有一個(gè)入度為零的頂點(diǎn),且其它頂點(diǎn)的入度均為1,則稱(chēng)這個(gè)有向圖為有向樹(shù)。一個(gè)有向圖的生成森林由若干棵有向樹(shù)組成,生成森林含有圖中所有的頂點(diǎn),且只有足以構(gòu)成若干棵互不相交的有向樹(shù)的弧。圖的基本操作圖的基本操作一般有:(1)InsertVertex(vertex):在圖中插入一個(gè)頂點(diǎn)vertex。(2)InsertEdge(v1,v2):在圖中插入一條邊(v1,v2)。(3)DeleteVertex(v):在圖中刪除一個(gè)頂點(diǎn)v及依附于v的邊。(4)DeleteEdge(v1,v2):在圖中刪除一條邊(或?。#?)GetWeight(v1,v2):在圖中取邊(v1、v2)上的權(quán)。(6)GetFirstNeighbor(intv):在圖中取頂點(diǎn)v的第一個(gè)鄰接點(diǎn)。(7)GetNextNeighbor(intv1,intv2):在圖中取頂點(diǎn)v1的在v2后的下一個(gè)鄰接點(diǎn)。(8)Travers():遍歷圖,按某種次序依次訪(fǎng)問(wèn)圖中的每一個(gè)頂點(diǎn),并使每一個(gè)頂點(diǎn)只被訪(fǎng)問(wèn)一次。(9)IsEmpty():判斷圖是否為空。
由于在圖中,任何兩個(gè)頂點(diǎn)之間都可能存在聯(lián)系,所以無(wú)法在存儲(chǔ)位置上反映數(shù)據(jù)元素之間的聯(lián)系,因此圖沒(méi)有順序存貯結(jié)構(gòu)。
按圖中頂點(diǎn)之間的聯(lián)系,圖的存儲(chǔ)結(jié)構(gòu)似乎采用多重鏈表表示比較恰當(dāng)。
但是若采用多重鏈表,則鏈表中結(jié)點(diǎn)的結(jié)構(gòu)難以確定。因?yàn)榻Y(jié)點(diǎn)中的指針數(shù)若按頂點(diǎn)度的最大值來(lái)設(shè)置,則會(huì)浪費(fèi)空間,因?yàn)橛泻芏囗旤c(diǎn)的度小于最大值;若頂點(diǎn)的指針數(shù)按每個(gè)頂點(diǎn)的度數(shù)來(lái)設(shè)置,則存儲(chǔ)結(jié)構(gòu)中會(huì)有很多頂點(diǎn)的結(jié)構(gòu)不一致,給圖的運(yùn)算帶來(lái)困難。因此,圖的存儲(chǔ)結(jié)構(gòu)不宜采用多重鏈表。
圖的存貯結(jié)構(gòu)應(yīng)根據(jù)具體問(wèn)題的要求來(lái)設(shè)計(jì)。常用的存貯結(jié)構(gòu)有鄰接矩陣、鄰接表、鄰接多重表和十字鏈表。
圖的存儲(chǔ)結(jié)構(gòu)
鄰接矩陣在圖的鄰接矩陣表示中,除了記錄每一個(gè)頂點(diǎn)信息的頂點(diǎn)表外,還有一個(gè)表示各個(gè)頂點(diǎn)之間關(guān)系的矩陣,稱(chēng)之為鄰接矩陣。若設(shè)圖G=(V,E)是一個(gè)有n個(gè)頂點(diǎn)的圖,則圖的鄰接矩陣是一個(gè)二維數(shù)組Arcs[n][n],它的定義為:對(duì)于網(wǎng)絡(luò)(或帶權(quán)圖),鄰接矩陣定義如下:
下圖給出了無(wú)向圖、有向圖和有向網(wǎng)的鄰接矩陣。其中一維數(shù)組Vertexes[]用于存儲(chǔ)頂點(diǎn)的信息,二維數(shù)組Arcs[][]用于存儲(chǔ)邊(或?。┑男畔?。
從圖中可知,無(wú)向圖的鄰接矩陣是對(duì)稱(chēng)的,將第i行的元素值或第i列的元素值累加起來(lái)就得到頂點(diǎn)i的度。
有向圖的鄰接矩陣可能不對(duì)稱(chēng)。如果第i行第j列為1,則表示有一條從頂點(diǎn)Vi到頂點(diǎn)Vj的有向邊,將第j列的所有元素值累加起來(lái)就得到頂點(diǎn)Vj的入度ID(Vj);將第i行的所有元素值累加起來(lái)就得到頂點(diǎn)Vi的出度OD(Vi)。即鄰接矩陣作為存儲(chǔ)表示的圖的類(lèi)聲明:const
intMaxVertexes=20;//最大的頂點(diǎn)數(shù)template<classvertexType,classarcType>classGraph{private:SeqList<vertexType>Vertexes(MaxVertexes);//存儲(chǔ)頂點(diǎn)信息的順序表
arcTypeArcs[MaxVertexes][MaxVertexes];//存儲(chǔ)邊(或弧)信息的矩陣
intCurrentNumArcs;//當(dāng)前的邊(或?。?shù)
intFindVertex(SeqList<vertexType>&L;constvertexType&v)//查找頂點(diǎn)v是否存在{returnL.Find(v);}
intGetVertexPos(constvertexType&v)//取頂點(diǎn)v在數(shù)組中的位置{returnFindVertex(Vertexes,v);}public:Graph(
intnum=MaxVertexes);//構(gòu)造函數(shù)
intIsEmpty()const{returnVertexes.IsEmpty();}
//判斷圖是否為空
intNumberOfVertexes(){returnVertexes.size;}
//取圖的頂點(diǎn)數(shù)
intNumberOfArcs(){returnCurrentNumArcs;}//取圖的邊(或?。?shù)
vertexTypeGetValue(
intv)
//取圖中頂點(diǎn)v的值,如果不存在頂點(diǎn)v則返回空
arcTypeGetWeight(
intv1,
intv2);
//取邊(v1,v2)(或弧<v1,v2>)上的權(quán)
intGetFirstNeighbor(
intv);//取圖中頂點(diǎn)v的第一個(gè)鄰接點(diǎn)的序號(hào)。如果不存在頂點(diǎn)v返回-1
intGetNextNeighbor(
intv1,
intv2);//取圖中頂點(diǎn)v1的在v2之后的下一個(gè)鄰接點(diǎn)的序號(hào)。如果不存在返回-1
intInsertVertex(vertexType&v);//在圖中插入頂點(diǎn)v
intInsertArc(
intv1,
intv2,arcTypeweight);//在圖中插入依附于頂點(diǎn)v1和v2的邊(或弧),weight是相應(yīng)邊(或弧)的信息
voidDeleteVertex(
intv);
//刪除頂點(diǎn)v,及依附于v的邊(或?。?/p>
voidDeleteArc(
intv1,
intv2);//在圖中刪除依附于頂點(diǎn)v1和v2的邊(或弧)}在鄰接矩陣上,圖的部分成員函數(shù)的實(shí)現(xiàn):template<classvertexType,classarcType>Graph<vertexType,arcType>::Graph(
intnum){//構(gòu)造函數(shù)
for(
inti=0;i<num;i++)//鄰接矩陣初始化
for(
intj=0;j<num;j++)arc[i][j]=0;CurrentNumArcs=0;//當(dāng)前邊(或弧)數(shù)為0}template<classvertexType,classarcType>
vertexTypeGraph<vertexType,arcType>::GetValue(
intv){//取圖中第v個(gè)頂點(diǎn)
if(v<0||v>=Vertexes.len)//如果頂點(diǎn)v不存在,則返回空
return
NULL;
else
returnVertexes.GetData[v];}template<classvertexType,classarcType>arcTypeGraph<vertexType,arcType>::GetWeight(
intv1,
intv2){//取出以頂點(diǎn)v1和v2為兩端點(diǎn)的邊上的權(quán)值
if(v1<0||v1>=Vertexes.len||v2<0||v2>Vertexes.len)
return-1;//如果頂點(diǎn)v1或v2不存在,則返回-1
else
returnArcs[v1][v2];}template<classvertexType,classarcType>intGraph<vertexType,arcType>::GetFirstNeighbor(const
intv){//取出頂點(diǎn)位置為v的第一個(gè)鄰接頂點(diǎn)的位置
if(v>=0&&v<Vertexes.len){
for(
intj=0;j<Vertexes.len;j++)
if(Arcs[v][j]==1)
returnj}
return
-1;}template<classvertexType,classarcType>
intGraph<vertexType,arcType>::GetNextNeighbor(
intv1,
intv2){//取圖中頂點(diǎn)v1的在v2之后的下一個(gè)鄰接點(diǎn)的序號(hào)。如果不存在返回-1
if(v1>=0&&v1<Vertexes.len
&&v2>=0&&v2<Vertexes.len){
for(
intj=v2+1;j<Vertexes.len;col++)
if(Arcs[v1][j]==1)
returnj;}
return
-1;}template<classvertexType,classarcType>
intGraph<vertexType,arcType>::InsertVertex(vertexType&v){//在圖中插入頂點(diǎn)v。插入成功,返回1;否則返回0
returnVertexes.insert(v,Vertexes.len);//在頂點(diǎn)數(shù)組Vertexes的最后插入v}template<classvertexType,classarcType>
intGraph<vertexType,arcType>::InsertArc(
intv1,intv2,arcTypeweight){//在圖中插入弧<v1v2>。插入成功,返回1;否則返回0。weight是相應(yīng)弧的信息
if(v1<0||v1>=Vertexes.len||v2<0||v2>Vertexes.len)//如果頂點(diǎn)v1或v2不存在,則返回0
return0;Arcs[v1][v2]=weight;CurrentNumArcs++;
return1;}template<classvertexType,classarcType>voidGraph<vertexType,arcType>::DeleteVertex(
intv){//刪除頂點(diǎn)v,及依附于v的弧
for(
inti=0;i<Vertexes.len;i++)//鄰接矩陣初始化
for(
intj=0;j<Vertexes.len;j++)
if(i==v||j==v)&&arc[i][j]!=0){arc[i][j]=0;CurrentNumArcs--;}Vertexes.Delete(v);}template<classvertexType,classarcType>voidGraph<vertexType,arcType>::DeleteArc(
intv1,
intv2);//在圖中刪除弧<v1,v2>
if(v1<0||v1>=Vertexes.len||v2<0||v2>Vertexes.len)
return0;//如果頂點(diǎn)v1或v2不存在,則返回0
Arcs[v1][v2]=0;CurrentNumArcs--;
return1;}鄰接表
鄰接表是鄰接矩陣的改進(jìn)。它把鄰接矩陣的每一行改為一個(gè)單鏈表。
對(duì)于無(wú)向圖,把依附于同一個(gè)頂點(diǎn)的邊鏈接在同一個(gè)單鏈表中稱(chēng)為邊鏈表,邊鏈表中的每一個(gè)結(jié)點(diǎn)代表一條邊叫做邊結(jié)點(diǎn)。在邊結(jié)點(diǎn)中保存著與該邊相關(guān)聯(lián)的另一頂點(diǎn)的序號(hào)和指向同一鏈表中下一個(gè)邊結(jié)點(diǎn)的指針;
對(duì)于有向圖,把從同一個(gè)頂點(diǎn)發(fā)出的弧鏈接在同一個(gè)單鏈表中稱(chēng)為弧鏈表,弧鏈表的每一個(gè)結(jié)點(diǎn)代表一條弧叫做弧結(jié)點(diǎn),在弧結(jié)點(diǎn)中保存著該弧的弧頭頂點(diǎn)序號(hào)和指向同一鏈表中下一個(gè)弧結(jié)點(diǎn)的指針。如果是帶權(quán)圖,則在邊(?。┙Y(jié)點(diǎn)中還要保存該邊(或弧)上的權(quán)值。
在鄰接表中,對(duì)于圖中的每一個(gè)頂點(diǎn)也用一個(gè)結(jié)點(diǎn)表示,稱(chēng)為頂點(diǎn)結(jié)點(diǎn)。頂點(diǎn)結(jié)點(diǎn)中保存了該頂點(diǎn)的信息和指向該頂點(diǎn)相應(yīng)的邊(?。╂湵淼闹羔槨K械捻旤c(diǎn)結(jié)點(diǎn)用順序表存儲(chǔ),并假設(shè)頂點(diǎn)的序號(hào)為數(shù)組的下標(biāo)。
下圖給出了無(wú)向圖的鄰接表表示。從無(wú)向圖的鄰接表中可以看到,同一條邊在鄰接表中出現(xiàn)了兩次,這是因?yàn)?vi,vj)與(vj,vi)雖是同一條邊,但在鄰接表中,(vi,vj)對(duì)應(yīng)的邊結(jié)點(diǎn)在頂點(diǎn)vi的邊鏈表中;(vj,vi)對(duì)應(yīng)的邊結(jié)點(diǎn)在頂點(diǎn)vj的邊鏈表中。如果想知道頂點(diǎn)vi的度,只需統(tǒng)計(jì)頂點(diǎn)vi的邊鏈表中邊結(jié)點(diǎn)的個(gè)數(shù)即可。
下圖(b)所示的是有向圖的鄰接表表示。在有向圖的鄰接表中,一條弧在鄰接表中只出現(xiàn)一次。如果統(tǒng)計(jì)頂點(diǎn)vi的弧鏈表中的弧結(jié)點(diǎn)個(gè)數(shù),只能得到該頂點(diǎn)的出度,所以這種鏈表也稱(chēng)為出弧表;若想要知道該頂點(diǎn)的入度,必須檢測(cè)其它所有的弧鏈表,看有多少個(gè)弧結(jié)點(diǎn)的弧頭頂點(diǎn)序號(hào)為vi,顯然,這是十分不方便的。
為此,對(duì)有向圖可以建立逆鄰接表,如圖(c)所示。在有向圖的逆鄰接表中,頂點(diǎn)vi的弧鏈表中鏈接的是所有進(jìn)入該頂點(diǎn)的弧,所以也稱(chēng)為入弧表。
對(duì)于帶權(quán)圖(網(wǎng)絡(luò)),必須在鄰接表的邊結(jié)點(diǎn)中增加一個(gè)存放邊上的權(quán)值的域weight。如下圖所示的是一個(gè)帶權(quán)圖的鄰接表表示。圖的鄰接表表示的類(lèi)定義:const
intMaxVertexes=20;//最大的頂點(diǎn)數(shù)template<classvertexType,classarcType>classGraphtemplate<classarcType>
structArcNode{//定義邊結(jié)點(diǎn)friend
classGraph<classvertexType,classarcType>;
intadjvex;//和邊(或?。┫嚓P(guān)聯(lián)的另一個(gè)頂點(diǎn)序號(hào)
arcTypeweight;//邊(或弧)上的信息(權(quán))
ArcNode<arcType>*nextarc;//指向下一條邊結(jié)點(diǎn)的指針
ArcNode(){}//構(gòu)造函數(shù)
ArcNode(
intv,arcTypew):adjvex(v),weight(w),next(NULL){}//構(gòu)造函數(shù)}template<classarcType,classvertexType>
structVertexNode{//定義頂點(diǎn)結(jié)點(diǎn)friend
classGraph<classvertexType,classarcType>;vertexTypedata;//頂點(diǎn)的信息
ArcNode<arcType>*firstarc;//指向依附該頂點(diǎn)的邊鏈表}template<classvertexType,classarcType>Graph{private:VertexNode<arcType,vertexType>*VertexesTable;//頂點(diǎn)表
intCurrentNumVertexes;當(dāng)前的頂點(diǎn)數(shù)
intCurrentNumArcs;//當(dāng)前的邊(或?。?shù)
intGetVertexPos(constvertexType&v);//取頂點(diǎn)v在數(shù)組中的位置public:Graph:CurrentNumVertexes(0),CurrentNumArcs(0){}Graph(vertexTypev[],
intnum=MaxVertexes);~Graph();//析構(gòu)函數(shù)
intIsEmpty()const{returnCurrentNumVertexes==0;}
intNumberOfVertexes(){returnCurrentNumVertexes;}
intNumberOfArcs(){returnCurrentNumArcs;}
vertexTypeGetValue(
intv)
arcTypeGetWeight(
intv1,
intv2);
intGetFirstNeighbor(
intv);
intGetNextNeighbor(
intv1,
intv2);
intInsertVertex(vertexType&v);
intInsertArc(
intv1,
intv2,arcTypew);
voidDeleteVertex(
intv);
voidDeleteArc(
intv1,
intv2);}在鄰接矩陣上,圖的部分成員函數(shù)的實(shí)現(xiàn):template<classvertexType,classarcType>intGraph<vertexType,arcType>::GetVertexPos(constvertexType&v){//根據(jù)頂點(diǎn)名vertex查找該頂點(diǎn)在鄰接表中的位置
for(
inti=0;i<CurrentNumVertex;i++)
if(VertexesTable[i].data==v)returni;
return-1;}template<classvertexType,classarcType>Graph<vertexType,arcType>::Graph(vertexTypev[],
intnum=MaxVertexes):CurrentNumVertexes(0),CurrentNumArcs(0){
inte,t,h;
vertexTypetail,head;
arcTypew;VertexesTable=newVertexNode<arcType,vertexType>[MaxVertexes];//創(chuàng)建頂點(diǎn)表}for(
inti=0;i<num;i++)//輸入各頂點(diǎn)信息{InsertVertex(v[i]);}//在頂點(diǎn)表中插入頂點(diǎn)v[i]
cin>>e;//輸入邊的條數(shù)
for(i=0;i<e;i++){//逐條輸入邊
cin>>tail>>head>>w;//輸入一條邊
while((t=GetVertexPos(tail))==-1)
cout<<”輸入的頂點(diǎn)(tail)不存在”;
while((h=GetVertexPos(head))==-1)
cout<<”輸入的頂點(diǎn)(head)不存在”;;
InsertArc(k,j,weight);//插入一條邊}
template<classvertexType,classarcType>Graph<vertexType,arcType>::~Graph(){
for(
inti=0;i<CurrentNumVertexes;i++){ArcNode<ArcType>*p=VertexesTable[i].firstadj;
while(p!=NULL){//逐條邊釋放
VertexesTable[i].firstadj=p→nextarc;
deletep;p=VertexesTable[i].firstadj;}}
delete[]VertexesTable;//釋放頂點(diǎn)表}template<ClassvertexType,classarcType>
vextexTypeGraph<vertexType,arcType>::GetValue(
intv){//取圖中頂點(diǎn)v的值,如果不存在頂點(diǎn)v則返回空
if(v>=0&&v<CurrentNumVertexes)
return
VertexesTable[v].data;
return
NULL;}template<ClassvertexType,classarcType>arcTypeGraph<vertexType,arcType>::GetWeight(
intv1,
intv2)
if(v1>=0&&v1<CurrentNumVertexes&&v2>=0&&v2<CurrentNumVertexes){ArcNode<ArcType>*p=NodeTable[v1].firstarc;
while(p!=NULL){
if(p→adjvex==v2)returnp→weight;
elsep=p→nextarc
}}
return
NULL;}template<ClassvertexType,classarcType>
intGraph<vertexType,arcType>::GetFirstNeighbor(
intv){
if(v>=0&&v<CurrentNumVertexes){
ArcNode<ArcType>*p=VertexesTable[v].firstadj;
if(p!=NULL)
returnp→adjvex;}
return–1;//若頂點(diǎn)不存在或頂點(diǎn)無(wú)關(guān)聯(lián)的邊}template<ClassvertexType,classarcTypeType>
intGraph<vertexType,arcType>::GetNextNeighbor(
intv1,
intv2){
if(v1!=-1){Edge<ArcType>*p=VertexesTable[v1].firstadj;
while(p!=NULL){
if(p→adjvex==v2&&p→nextarc!=NULL)
returnp→nextarc→adjvex;
elsep=p→nextarc;}}
return-1;//沒(méi)有查到下一個(gè)鄰接頂點(diǎn)返回-1}template<ClassvertexType,classarcTypeType>
intGraph<vertexType,arcType>::
InsertVertex(vertexType&v){
if(CurrentNumVertexes<MaxVertexes-1){
VertexesTable[CurrentNumVertexes].data=v;VertexesTable[CurrentNumVertexes].firstadj=NULL;CurrentNumVertexes++;
return1;}
return0;//否則返回0}鄰接多重表
在無(wú)向圖的鄰接多重表中,圖的每一條邊用一個(gè)邊結(jié)點(diǎn)表示,它由六個(gè)域組成。
其中,tag是標(biāo)記域,標(biāo)記該邊是否被處理或被搜索過(guò);wieght為邊的信息域,用于存儲(chǔ)邊的權(quán)值;adjvex1和adjvex2是頂點(diǎn)域,表示該邊所依附的兩個(gè)頂點(diǎn)在圖中的序號(hào);
nextarc1域是鏈接指針,指向下一條依附于頂點(diǎn)adjvex1的邊;nextarc2也是鏈接指針,指向下一條依附于頂點(diǎn)adjvex2的邊。對(duì)圖中的每一個(gè)頂點(diǎn)用一個(gè)頂點(diǎn)結(jié)點(diǎn)表示,它有兩個(gè)域組成。其中,data域存儲(chǔ)有關(guān)頂點(diǎn)的信息;firstarc域是鏈接指針,指向第一條依附于該頂點(diǎn)的邊。所有的頂點(diǎn)結(jié)點(diǎn)組成一個(gè)順序表。下圖所示,給出了無(wú)向圖的鄰接多重表表示,及其這兩種結(jié)點(diǎn)的示例。在無(wú)向圖的鄰接多重表中,所需存儲(chǔ)空間與表示無(wú)向圖的鄰接表相同。
下面給出鄰接多重表的類(lèi)定義:const
intMaxVertexes=20;//最大的頂點(diǎn)數(shù)template<classvertexType,classarcType>classGraphtemplate<classarcType>
structArcNode{friend
classGraph<classvertexType,classarcType>;
inttag;//邊的處理標(biāo)志
arcTypeweight;//邊(或弧)上的信息(權(quán))
intadjvex1,adjvex2;//和邊相關(guān)聯(lián)的兩個(gè)頂點(diǎn)序號(hào)
ArcNode<arcType>*nextarc1,*nextarc2;}template<classarcType,classvertexType>
structVertexNode{//定義頂點(diǎn)結(jié)點(diǎn)friend
classGraph<classvertexType,class
arcType>;vertexTypedata;//頂點(diǎn)的信息
ArcNode<arcType>*firstarc;//指向依附該頂點(diǎn)的邊(或?。╂湵淼念^}template<classvertexType,classarcType>Graph{private:VertexNode<arcType,vertexType>*VertexesTable;//頂點(diǎn)表
intCurrentNumVertexes;當(dāng)前的頂點(diǎn)數(shù)
intCurrentNumArcs;//當(dāng)前的邊(或?。?shù)
intGetVertexPos(constvertexType&v);public:Graph:CurrentNumVertexes(0),CurrentNumArcs(0){}Graph(vertexTypev[],
intnum=MaxVertexes);//構(gòu)造函數(shù)~Graph();//析構(gòu)函數(shù)}十字鏈表
在有向圖的十字鏈表中,圖中的每一條弧用一個(gè)弧結(jié)點(diǎn)表示?;〗Y(jié)點(diǎn)的結(jié)構(gòu)與無(wú)向圖鄰接多重表中的邊結(jié)點(diǎn)結(jié)構(gòu)類(lèi)似,也有六個(gè)域。其中,tag是標(biāo)記域,標(biāo)記該弧是否被處理或被搜索過(guò);wieght為弧的信息域,用于存儲(chǔ)弧的權(quán)值等信息;tailvex和headvex是分別表示弧尾頂點(diǎn)序號(hào)和弧頭頂點(diǎn)序號(hào)的頂點(diǎn)域;tailnextarc域是鏈接指針,指向下一條以頂點(diǎn)tailvex為始點(diǎn)(弧尾)的??;
eadnextarc也是鏈接指針,指向下一條以頂點(diǎn)headvex為終點(diǎn)(弧頭)的弧。對(duì)有向圖中的每一個(gè)頂點(diǎn)也用一個(gè)頂點(diǎn)結(jié)點(diǎn)表示,它由三個(gè)域組成。其中,data域存儲(chǔ)有關(guān)頂點(diǎn)的信息;firstinarc域是鏈接指針,指向第一條以該頂點(diǎn)為終點(diǎn)的?。籪irstoutarc域也是鏈接指針,指向第一條以該頂點(diǎn)為始點(diǎn)的弧。所有的頂點(diǎn)結(jié)點(diǎn)組成一個(gè)順序表。
下圖給出了有向圖十字鏈表表示示例及其頂點(diǎn)結(jié)點(diǎn)和弧結(jié)點(diǎn)的結(jié)構(gòu)。
在有向圖的十字鏈表中,從頂點(diǎn)結(jié)點(diǎn)中的firstoutarc域出發(fā),由弧結(jié)點(diǎn)中的tailnextarc域鏈接起來(lái)的鏈表,正好是原來(lái)的鄰接表結(jié)構(gòu)。統(tǒng)計(jì)該鏈表中弧結(jié)點(diǎn)的個(gè)數(shù),可求得該頂點(diǎn)的出度。若從頂點(diǎn)結(jié)點(diǎn)的firstoutarc域出發(fā),由弧結(jié)點(diǎn)中的headnextarc域鏈接起來(lái)的鏈表,正好是原來(lái)的逆鄰接表結(jié)構(gòu)。統(tǒng)計(jì)該鏈表中弧結(jié)點(diǎn)的個(gè)數(shù),可求得該頂點(diǎn)的入度。圖的遍歷與連通性
對(duì)于給定的圖,沿著一些邊(或弧)訪(fǎng)問(wèn)圖中所有的頂點(diǎn),且使每個(gè)頂點(diǎn)僅被訪(fǎng)問(wèn)一次,這個(gè)過(guò)程叫做圖的遍歷。
圖的遍歷通常有兩種方法:深度優(yōu)先遍歷(DepthFirstTraversal)和廣度優(yōu)先遍歷(BreadthFirstTraversal)。這兩種方法對(duì)無(wú)向圖和有向圖都是適用的,但在下面的討論中將主要介紹對(duì)無(wú)向圖的遍歷。深度優(yōu)先遍歷
圖的深度優(yōu)先遍歷基于深度優(yōu)先搜索DFS(DepthFirstSearch),
深度優(yōu)先搜索是從圖中某一頂點(diǎn)v出發(fā),在訪(fǎng)問(wèn)頂點(diǎn)v后,再依次從v的任一還沒(méi)有被訪(fǎng)問(wèn)的鄰接頂點(diǎn)w出發(fā)進(jìn)行深度優(yōu)先搜索,直到圖中所有與頂點(diǎn)v由路徑相通的頂點(diǎn)都被訪(fǎng)問(wèn)過(guò)為止。這是一個(gè)遞歸定義,所以圖的深度優(yōu)先搜索可以用遞歸算法實(shí)現(xiàn)。
下圖(a)給出了深度優(yōu)先搜索的示例。由于該圖是連通的,所以從頂點(diǎn)A出發(fā),通過(guò)一次深度優(yōu)先搜索,就可以訪(fǎng)問(wèn)圖中的所有頂點(diǎn)。
圖的深度優(yōu)先遍歷的訪(fǎng)問(wèn)順序與樹(shù)的前序遍歷順序類(lèi)似。圖(b)給出了在深度優(yōu)先遍歷的過(guò)程中,訪(fǎng)問(wèn)的所有頂點(diǎn)和經(jīng)過(guò)的邊,圖中各頂點(diǎn)旁附加的數(shù)字表示各頂點(diǎn)被訪(fǎng)問(wèn)的次序。在圖(b)中,共有n-1條邊連結(jié)了所有n個(gè)頂點(diǎn),在此把它稱(chēng)為圖(a)的深度優(yōu)先搜索生成樹(shù)。從指定的結(jié)點(diǎn)v開(kāi)始進(jìn)行深度優(yōu)先搜索的算法的步驟是:(1)訪(fǎng)問(wèn)結(jié)點(diǎn)v,并標(biāo)記v已被訪(fǎng)問(wèn);(2)取頂點(diǎn)v的第一個(gè)鄰接頂點(diǎn)w;(3)若頂點(diǎn)w不存在,算法結(jié)束;否則繼續(xù)步驟(4)(4)若頂點(diǎn)w未被訪(fǎng)問(wèn),則訪(fǎng)問(wèn)結(jié)點(diǎn)w,并標(biāo)記w已被訪(fǎng)問(wèn);(5)使w為頂點(diǎn)v的在原來(lái)w之后的下一個(gè)鄰接頂點(diǎn),轉(zhuǎn)到步驟(3)。下面給出深度優(yōu)先搜索和深度優(yōu)先遍歷的算法:template<classvertexType,classarcType>voidGraph<vertexType,arcType>::DFTraverse(voidvisit(vertexTypev)){
inti,n=NumberOfVertexes();//取圖的頂點(diǎn)個(gè)數(shù)
int*visited=new
int[n];//定義訪(fǎng)問(wèn)標(biāo)記數(shù)組
visited
for(i=0;i<n;i++)visited[i]=0;//訪(fǎng)問(wèn)標(biāo)記數(shù)組visited初始化
for(i=0;i<n;i++)//對(duì)圖中的每一個(gè)頂點(diǎn)進(jìn)行判斷
if(!visited[i])DFS(v,visited,visit);
delete[]visited;//釋放
visited}template<classvertexType,class
arcType>void
Graph<vertexType,arcType>::DFS(const
intv,
intvisited[],voidvisit(vertexTypev)){
visit(GetValue(v));//訪(fǎng)問(wèn)頂點(diǎn)
vvisited[v]=1;//頂點(diǎn)v作訪(fǎng)問(wèn)標(biāo)記
intw=GetFirstNeighbor(v);
while(w!=-1){//若頂點(diǎn)w存在
if(!visited[w])DFS(w,visited);
w=GetNextNeighbor(v,w);}//重復(fù)檢測(cè)v的所有鄰接頂點(diǎn)}廣度優(yōu)先遍歷
圖的廣度優(yōu)先遍歷基于廣度優(yōu)先搜索BFS(BreadthFirstSearch),
廣度優(yōu)先搜索是從圖中某一頂點(diǎn)v出發(fā),在訪(fǎng)問(wèn)頂點(diǎn)v后再訪(fǎng)問(wèn)v的各個(gè)未曾被訪(fǎng)問(wèn)過(guò)的鄰接頂點(diǎn)w1、w2、…、wk,然后再依次訪(fǎng)問(wèn)w1、w2、…、wk的所有還未被訪(fǎng)問(wèn)過(guò)的鄰接頂點(diǎn)。再?gòu)倪@些訪(fǎng)問(wèn)過(guò)的頂點(diǎn)出發(fā),再訪(fǎng)問(wèn)它們的所有還未被訪(fǎng)問(wèn)過(guò)的鄰接頂點(diǎn),……,如此下去,直到圖中所有和頂點(diǎn)v由路徑連通的頂點(diǎn)都被訪(fǎng)問(wèn)到為止。
下圖(a)給出了一個(gè)從頂點(diǎn)A出發(fā)進(jìn)行廣度優(yōu)先遍歷的示例。圖(b)給出了由廣度優(yōu)先遍歷得到的廣度優(yōu)先生成樹(shù),它由遍歷時(shí)訪(fǎng)問(wèn)過(guò)的n個(gè)頂點(diǎn)和遍歷時(shí)經(jīng)歷的n-1條邊組成,各頂點(diǎn)旁邊附的數(shù)字標(biāo)明了頂點(diǎn)被訪(fǎng)問(wèn)的順序。
從指定的結(jié)點(diǎn)v開(kāi)始進(jìn)行廣度優(yōu)先搜索的算法步驟是:(1)訪(fǎng)問(wèn)結(jié)點(diǎn)v,并標(biāo)記v已被訪(fǎng)問(wèn),同時(shí)頂點(diǎn)v入隊(duì)列;(2)當(dāng)隊(duì)列空時(shí)算法結(jié)束,否則繼續(xù)步驟(3);(3)隊(duì)頭頂點(diǎn)出隊(duì)列為v;(4)取頂點(diǎn)v的第一個(gè)鄰接頂點(diǎn)w;(5)若頂點(diǎn)w不存在,轉(zhuǎn)步驟(3);否則繼續(xù)步驟(6)(6)若頂點(diǎn)w未被訪(fǎng)問(wèn),則訪(fǎng)問(wèn)結(jié)點(diǎn)w,并標(biāo)記w已被訪(fǎng)問(wèn),同時(shí)頂點(diǎn)w入隊(duì)列;否則繼續(xù)步驟(7);(7)使w為頂點(diǎn)v的在原來(lái)w之后的下一個(gè)鄰接頂點(diǎn),轉(zhuǎn)到步驟(5)。廣度優(yōu)先遍歷的算法:template<classvertexType,classarcType>voidGraph<vertexType,arcType>::BFTraverse(voidvisit(vertexTypev)){
inti,n=NumberOfVertexes();//取圖的頂點(diǎn)個(gè)數(shù)
int*visited=new
int[n];//定義訪(fǎng)問(wèn)標(biāo)記數(shù)組visited
for(i=0;i<n;i++)visited[i]=0;//訪(fǎng)問(wèn)標(biāo)記數(shù)組visited初始化
for(i=0;i<n;i++)//對(duì)圖中的每一個(gè)頂點(diǎn)進(jìn)行判斷
if(!visited[i])BFS(i,visited,visit);
delete[]visited;//釋放visited}template<classvertexType,classarcType>voidGraph<vertexType,arcType>::BFS(const
intv,
intvisited[],voidvisit(vertexTypev)){
linkqueue<int>q;//定義隊(duì)列qvisit(GetValue(v));//訪(fǎng)問(wèn)頂點(diǎn)vvisited[v]=1;//頂點(diǎn)v作已訪(fǎng)問(wèn)標(biāo)記
q.EnQueue(v);//頂點(diǎn)v進(jìn)隊(duì)列q
while(!q.IsEmpty()){
v=q.DeQueue();//否則,隊(duì)頭元素出隊(duì)列
intw=GetFirstNeighbor(v);while(w!=-1){//若鄰接頂點(diǎn)w存在
if(!visited[w]){//若該鄰接頂點(diǎn)未訪(fǎng)問(wèn)過(guò)
visit(GetValue(w));//訪(fǎng)問(wèn)頂點(diǎn)wvisited[w]=1;//頂點(diǎn)w作已訪(fǎng)問(wèn)標(biāo)記
q.EnQueue(w);//頂點(diǎn)w進(jìn)隊(duì)列q}w=GetNextNeighbor(v,w);}//重復(fù)檢測(cè)v的所有鄰接頂點(diǎn)
}//外層循環(huán),判隊(duì)列空否}連通分量對(duì)于連通圖,從任一頂點(diǎn)出發(fā),只需一次調(diào)用深度優(yōu)先搜索算法或廣度優(yōu)先搜索算法就可以訪(fǎng)問(wèn)到圖中的所有頂點(diǎn);
對(duì)于非連通圖時(shí),從圖中某一頂點(diǎn)出發(fā),一次調(diào)用深度優(yōu)先搜索算法或廣度優(yōu)先搜索算法不可能訪(fǎng)問(wèn)到圖中的所有頂點(diǎn),只能訪(fǎng)問(wèn)到該頂點(diǎn)所在的極大連通子圖(即連通分量)的所有頂點(diǎn)。非連通圖有n個(gè)連通分量,就要n次調(diào)用DFS或BFS才能訪(fǎng)問(wèn)圖中的所有頂點(diǎn)。若從圖的每一個(gè)連通分量中的一個(gè)頂點(diǎn)出發(fā)進(jìn)行搜索,就可以求得圖的所有連通分量。所謂連通分量是指非連通圖中極大連通子圖。
最小生成樹(shù)
一個(gè)連通圖的生成樹(shù)是原圖的極小連通子圖,它包含原圖中的所有n個(gè)頂點(diǎn)和使n個(gè)頂點(diǎn)連通的n-1條邊。
這意味著對(duì)于生成樹(shù)來(lái)說(shuō),若刪除它的一條邊,就會(huì)使生成樹(shù)變成非連通圖;若給它增加一條邊,就會(huì)形成圖中的一個(gè)回路。
另一方面,一個(gè)連通圖的生成樹(shù)不是唯一的,使用不同的方法遍歷圖,可以得到不同的生成樹(shù);從不同的頂點(diǎn)出發(fā),也可能得到不同的生成樹(shù);而且生成樹(shù)有時(shí)還和圖的存儲(chǔ)結(jié)構(gòu)中具體的結(jié)點(diǎn)順序有關(guān)。
對(duì)于一個(gè)帶權(quán)的連通圖(即網(wǎng)絡(luò)),如何找出一棵生成樹(shù),使得各邊上的權(quán)值總和達(dá)到最小,這是一個(gè)有實(shí)際意義的問(wèn)題。對(duì)于這樣一個(gè)有n個(gè)頂點(diǎn)的網(wǎng)絡(luò),可以有不同的生成樹(shù),每一棵生成樹(shù)都可以構(gòu)成通信網(wǎng)絡(luò)。我們希望能夠根據(jù)各條邊上的權(quán)值,選擇一棵總造價(jià)最小的生成樹(shù),這就是構(gòu)造網(wǎng)絡(luò)的最小(代價(jià))生成樹(shù)(Minimum-costSpanningTree)問(wèn)題。
克魯斯卡爾算法
克魯斯卡爾(Kruskal)算法的基本思想是:
設(shè)一個(gè)有n個(gè)頂點(diǎn)的連通網(wǎng)絡(luò)G={V,E},最初先構(gòu)造一個(gè)包括全部n個(gè)頂點(diǎn)和0條邊的森林F={T0、T1、…、Tn-1},
以后每一步向F中加入一條邊(v、u),該邊應(yīng)當(dāng)是所依附的兩個(gè)頂點(diǎn)v和u分別在森林F的兩棵不同的樹(shù)上的所有邊中具有最小權(quán)值的邊。由于這條邊的加入,使F中的某兩棵樹(shù)合并為一棵,樹(shù)的棵數(shù)減一。如此,經(jīng)過(guò)n—1步,最終得到一棵有n—1條邊且各邊權(quán)值總和達(dá)到最小的生成樹(shù)——最小生成樹(shù)。對(duì)于前圖(a)所示的連通網(wǎng)絡(luò),下圖中(a)(f)給出了按克魯斯卡爾算法生成最小生成樹(shù)的過(guò)程。在克魯斯卡爾算法中,利用最小堆來(lái)存放連通網(wǎng)絡(luò)中的邊,堆中每個(gè)元素代表連通網(wǎng)絡(luò)中的一條邊,它有三個(gè)域組成:adjvex1、adjvex2和weight,其中adjvex1和adjvex2存儲(chǔ)該邊所依附的兩個(gè)頂點(diǎn)的序號(hào),weight存儲(chǔ)邊上的權(quán)值;在利用并查集存放所有連通分量,同一個(gè)連通分量的頂點(diǎn)組成并查集的一個(gè)子集(等價(jià)類(lèi))??唆斔箍査惴ú襟E如下:(1)初始化,在并查集中,連通網(wǎng)絡(luò)的每一個(gè)頂點(diǎn)獨(dú)立成一個(gè)等價(jià)類(lèi),連通網(wǎng)絡(luò)的所有的邊建立最小堆,最小生成樹(shù)T中沒(méi)有任何邊,T中邊的條數(shù)計(jì)數(shù)器i為0;(2)如果T中邊的條數(shù)計(jì)數(shù)器i等于頂點(diǎn)數(shù)減1,則算法結(jié)束;否則繼續(xù)步驟(3);(3)選取堆頂元素代表的邊(v,u),同時(shí)調(diào)整堆;(4)利用并查集的運(yùn)算檢查依附于邊(v,u)的兩個(gè)頂點(diǎn)v和u是否在同一個(gè)連通分量(即并查集的同一個(gè)子集合)上,如果是則轉(zhuǎn)步驟(2);否則繼續(xù)步驟(5);(5)將邊(v,u)加入到最小生成樹(shù)T中,同時(shí)將這兩個(gè)頂點(diǎn)所在的連通分量合并成一個(gè)連通分量(即并查集中的相應(yīng)兩個(gè)子集合并成一個(gè)子集),繼續(xù)步驟(2)。最小生成樹(shù)的類(lèi)聲明:const
intMAXNUM=機(jī)器可表示的最大整數(shù)const
intMaxNumArc=20//圖中最大的邊數(shù)classMinSpanTree;classMSTArcNode{//生成樹(shù)邊結(jié)點(diǎn)的類(lèi)定義friend
classMinSpanTree;private:
intadjvex1,adjvex2;//一條邊所依附的兩個(gè)頂點(diǎn)
floatweight;//邊的代價(jià)(權(quán)值)};class
MinSpanTree{//生成樹(shù)的類(lèi)定義public:MinSpanTree():CurrentNumArc(0){arctable=newMSTArcNode[MaxNumArc];}
intInsert(MSTArcNode&e);//將邊e加到最小生成樹(shù)中protected:MSTArcNode*arctable;//存放邊的數(shù)組
intCurrentNumArc;//當(dāng)前邊數(shù)};右圖給出了對(duì)于前圖(a)所示的連通網(wǎng)絡(luò),按克魯斯卡爾算法構(gòu)造最小生成樹(shù)時(shí)最小堆和并查集的變化過(guò)程。在初始建堆時(shí),邊的輸入順序?yàn)椋?A、B),(A、C),(A、F),(B、E),(C、D),(C、F),(D、E),(D、F),(E,F(xiàn))??唆斔箍査惴ǖ腃++描述:voidGraph<string,float>::Kruskal(MinSpanTree&T){
intv,u,i=1;MSTArcNodee;//邊結(jié)點(diǎn)輔助單元
MinHeap<MSTArcNode>h(CurrentNumArcs);
intNum=NumberOfVertexes();//取圖的頂點(diǎn)個(gè)數(shù)
UFSetsf(Vertexes,Num);
for(u=0;u<Num;u++)//建立初始最小堆h
for(v=u+1;v<Num;v++)
if(Arcs[u][v]!=MAXNUM){//把圖中的所有邊
e.adjvex1=u;e.adjvex2=v;e.weight=Arcs[u][v];h.Insert(e);//把e插入堆}while(i<Num){//最小生成樹(shù)中的邊數(shù)不到頂點(diǎn)數(shù)減一
e=h.DeleteTop();//從堆中退出一條邊
u=f.Find(e.adjvex1);//取兩個(gè)頂點(diǎn)所在的等價(jià)類(lèi)的根
v=f.Find(e.adjvex2);
if(u!=v){//如果兩個(gè)頂點(diǎn)不在同一連通分量
f.Union(u,v);//合并
T.Insert(e);//該邊存入最小生成樹(shù)Ti++;//計(jì)數(shù)器自增}}}普里姆算法
普里姆(Prim)算法的基本思想是:假設(shè)連通網(wǎng)絡(luò)為N=(V,E);TE為N的最小生成樹(shù)上邊的集合,開(kāi)始時(shí)TE=;U為算法在構(gòu)造最小生成樹(shù)過(guò)程中已得到的頂點(diǎn)集,開(kāi)始時(shí)U={u0}(u0V)。
算法從N中的某一頂點(diǎn)u0出發(fā),選擇與u0關(guān)聯(lián)的具有最小權(quán)值的邊(u0、vi),將頂點(diǎn)vi加入到生成樹(shù)的頂點(diǎn)集合U中,(u0,vi)加入到集合TE中,以后每一步從一個(gè)頂點(diǎn)在U中,而另一個(gè)頂點(diǎn)在V-U中的各條邊當(dāng)中選擇權(quán)值最小的邊(u、v)(uU,vV-U),把頂點(diǎn)v加入到集合U中,邊(v、u)加入到集合TE中。如此重復(fù),直到網(wǎng)絡(luò)中的所有頂點(diǎn)都加入到生成樹(shù)頂點(diǎn)集合U(U=V)中為止。此時(shí),TE中剛好有n-1條邊,則T=(V,TE)為N的最小生成樹(shù)。對(duì)于前圖(a)所示的連通網(wǎng)絡(luò),下圖(a)(f)給出了按普里姆算法從頂點(diǎn)A開(kāi)始構(gòu)造最小生成樹(shù)的過(guò)程。class
MinSpanTree{//生成樹(shù)的類(lèi)定義public:MinSpanTree():CurrentNumArc(0){arctable=newMSTArcNode[MaxNumArc];}
intInsert(MSTArcNode&e);//將邊e加到最小生成樹(shù)中protected:MSTArcNode*arctable;//存放邊的數(shù)組
intCurrentNumArc;//當(dāng)前邊數(shù)};在利用普里姆算法構(gòu)造最小生成樹(shù)過(guò)程中,需要設(shè)置了一個(gè)輔助數(shù)組closearc[],以記錄從V-U中頂點(diǎn)到U中頂點(diǎn)具有最小權(quán)值的邊。
對(duì)每一個(gè)頂點(diǎn)vV-U,在輔助數(shù)組中有一個(gè)分量closearc[v],它包括兩個(gè)域:lowweight和nearvertex。其中,lowweight中存放頂點(diǎn)v到U中的各頂點(diǎn)的邊上的當(dāng)前最小權(quán)值(lowweight=0表示vU);nearvertex記錄頂點(diǎn)v到U中具有最小權(quán)值的那條邊的另一個(gè)鄰接頂點(diǎn)u(nearvertex=-1表示該頂點(diǎn)v為開(kāi)始頂點(diǎn))。在下面的普里姆算法描述中,連通網(wǎng)絡(luò)采用鄰接矩陣作為存儲(chǔ)結(jié)構(gòu),并假設(shè)普里姆算法從頂點(diǎn)A(設(shè)頂點(diǎn)A的序號(hào)為0)出發(fā)(即u0=0)。普里姆算法步驟如下:(1)初始化輔助數(shù)組closearc[];(2)重復(fù)下列步驟(3)和(5)n-1次(3)在closearc[]中選擇lowweight0&&
lowweight最小的頂點(diǎn)v,即選中的權(quán)值最小的邊為(closearc[v].nearvertex,i)。(4)將closearc[v].lowweight改為0,表示頂點(diǎn)i已加入頂點(diǎn)集U中。并將邊(closearc[v].nearvertex、v)加入生成樹(shù)T的邊集合。(5)對(duì)V-U中的每一個(gè)頂點(diǎn)j,如果依附于頂點(diǎn)j和剛加入U(xiǎn)集合的新頂點(diǎn)v的邊的權(quán)值A(chǔ)rcs[v][j]小于原來(lái)依附于j和生成樹(shù)頂點(diǎn)集合中頂點(diǎn)的邊的最短距離closearc[j].lowweight,則修改closearc[j],使其lowweight=Arcs[v][j]},nearvertex=v。下圖給出了對(duì)于前圖(a)所示的連通網(wǎng)絡(luò),按普里姆算法構(gòu)造最小生成樹(shù)時(shí)輔助數(shù)組closearc[]的變化過(guò)程。普里姆算法的C++描述:classMinSpanTree;classCloseArcType{//輔助數(shù)組closearc[]的元素類(lèi)定義friend
classMinSpanTree;private:
floatlowweight;//邊的代價(jià)(權(quán)值)
intnearvertex;//U中的頂點(diǎn)};voidGraph<string,float>::Prim(MinSpanTree&T){
intNum=NumberOfVertexes();//取連通網(wǎng)絡(luò)的頂點(diǎn)個(gè)數(shù)
CloseArcType*closearc=newclosearctype[Num];
MSTArcNodee;//定義最小生成樹(shù)邊結(jié)點(diǎn)輔助變量
floatmin;
intv,i,j;
for(i=1;i<Num;i++){//初始化輔助數(shù)組closearc[]closearc[i].lowweight=Arcs[0][i];closearc[i].nearvertex=0;}closearc[0].lowweight=0;//頂點(diǎn)0加到生成樹(shù)頂點(diǎn)集合
closearc[0].nearvertex=-1;
for(i=1;i<Num;i++){//循環(huán)n-1次,加入n-1條邊
min=MAXNUM;v=0;
for(
intj=0;j<Num;j++)
if(closearc[j].lowweight!=0&&closearc[j].lowweight<min){v=j;min=closearc[j].lowweight;}//選取兩個(gè)鄰接頂點(diǎn)分別在U-V和U且具有最小權(quán)值的邊
if(v){//v==0表示再也找不到所求的邊
e.adjvex1=closearc[v].nearvertex;e.adjvex2=v;e.weight=closearc[v].lowweight;
T.Insert(e);//把選出的邊加入到生成樹(shù)中
closearc[v].lowweight=0;//把頂點(diǎn)v加入U(xiǎn)中
for(j=1;j<Num;j++)
if(closearc[j].lowweight!=0&&
Arcs[v][j]<closearc[j].lowweight){//對(duì)U-V中的每一個(gè)頂點(diǎn)考察是否要修改它在輔助數(shù)組中的值
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2022年北京市密云初三二模英語(yǔ)試卷及答案
- 電力儲(chǔ)能知識(shí)培訓(xùn)課件
- 2020-2021深圳安康學(xué)校初中部小學(xué)三年級(jí)數(shù)學(xué)上期末模擬試題及答案
- 罐清洗施工方案
- 水平挑網(wǎng)施工方案
- 養(yǎng)殖場(chǎng)黃魚(yú)買(mǎi)賣(mài)合同范本
- 加拿大勞務(wù)合同范例
- 各類(lèi)評(píng)審評(píng)估整改工作的總結(jié)計(jì)劃
- 學(xué)校藝術(shù)作品創(chuàng)作展的策劃計(jì)劃
- 探索幼兒園環(huán)境教育的工作計(jì)劃
- 礦產(chǎn)勘探數(shù)據(jù)分析-深度研究
- 人教版高中英語(yǔ)挖掘文本深度學(xué)習(xí)-選修二-UNIT-4(解析版)
- 2025年北京控股集團(tuán)有限公司招聘筆試參考題庫(kù)含答案解析
- 2025中智集團(tuán)招聘重要崗位高頻重點(diǎn)提升(共500題)附帶答案詳解
- 2025年人事科年度工作計(jì)劃
- 2023-2024學(xué)年高中信息技術(shù)必修一滬科版(2019)第二單元項(xiàng)目三《 調(diào)查中學(xué)生移動(dòng)學(xué)習(xí)現(xiàn)狀-經(jīng)歷數(shù)據(jù)處理的一般過(guò)程》說(shuō)課稿
- 2021年煤礦應(yīng)急資源調(diào)查報(bào)告
- 院感知識(shí)手衛(wèi)生培訓(xùn)內(nèi)容
- 產(chǎn)教融合咨詢(xún)協(xié)議書(shū)
- 外國(guó)文學(xué)課課程設(shè)計(jì)
- 《鐵路軌道維護(hù)》課件-直線(xiàn)撥道作業(yè)
評(píng)論
0/150
提交評(píng)論