日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【算法】图(一)拓扑排序的实现 图的邻接表算法 判断是否图G中存在环

發(fā)布時間:2024/9/30 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【算法】图(一)拓扑排序的实现 图的邻接表算法 判断是否图G中存在环 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

    • 用list來表示圖,判斷是否存在環(huán)
    • 鄰接表實現(xiàn)拓?fù)渑判?/li>
    • 用DFS(鄰接矩陣) 來實現(xiàn)拓?fù)渑判颉?/li>
    • 判斷無向圖頂點是否全部連通
    • 判斷圖G中從頂點u到v是否存在簡單路徑
    • 輸出圖G中從頂點u到v的所有簡單路徑
    • 判斷無向圖是否存在經(jīng)過頂點v的環(huán)
      • 輸出所有無向圖中經(jīng)過頂點v的環(huán)

用list來表示圖,判斷是否存在環(huán)

定義class Graphic,成員變量和方法如下:

class Graphic{ public: Graphic(int vertex){ this->vertexNum = vertex; adjacents = new list<int>[this->vertexNum]; //構(gòu)造圖 } int getVertexNum(){return this->vertexNum;//獲得頂點數(shù) } void setVertexNum(int number) {//修改頂點數(shù)this->vertexNum = number; } void addEdge(int start,int end) {this->adjacents[start].push_back(end); } void countOutdegree(Graphic*g, int *outdegree);//計算所有頂點的出度 bool isDAG(Graphic *g);//判斷是否為有向無環(huán)圖 bool deleteVertex(int v) {list<int>*p = &adjacents[v];//取出該list的頂點v的地址,獲取指定點的出度鏈表p->clear();delete p;//刪除指定的頂點及其出度邊//需對原來的圖結(jié)構(gòu)進(jìn)行修改for(int i = v;i < vertexNum -1;i++)adjacents[v] = adjacents[v+1];//相當(dāng)于全部元素向前移,因為刪除了一個this->vertexNum--;//頂點數(shù)也要修改 list<int>* newadj = new list[this->vertexNum]; for(int i = 0;i < this->vertexNum;i++) newadj[i] = adjacents[i];//將所有的原list中的元素賦值到一個臨時new的list中 list<int>*q = newadj; this->adjacents = newadj; delete q;//刪除q } private: int vertexNum; //頂點數(shù) list<int> *adjacents; //鄰接表存儲邊關(guān)系 注意它的類型,是一個list類型中存儲了很多個指針 }; void Graphic::countOutdegree(Graphic *g, int *indegree){int total = g->getVertexNum(); int v; list<int>::iterator it; for(v = 0;v < total;v++) {int number = 0;for(it =adjacents[v].begin(); it != adjacents[v].end() ;it++){number++;}indegree[v] = number; } } bool Graphic::isDAG(Graphic *graphic){int total = graphic->getVertexNum();int* outdegree = new int[total];for(int i = 0;i < total;i++)outdegree[i] = 0;//初始化數(shù)組Graphic::countOutdegree(graphic,outdegree); bool ifHas = false;//標(biāo)志,判斷是否存在出度為0的點 list<int>zeroOutDegree;//存儲所有出度為0的點 for(int i = 0;i < total;i++) {if(outdegree[i] == 0){zeroOutDegree.push_back(outdegree[i]);ifHas = true;} } if(ifHas)//如果有出度為0的邊 { graphic->deleteVertex(zeroOutDegree.front()); if(graphic->getVertexNum() > 0) {isDAG(graphic);//遞歸入口 } else return true;//遞歸判斷該圖 每次都刪除出度為0的點和出度邊 } else return false;//如果ifHas為False表示沒有出度為0的點 }

鄰接表實現(xiàn)拓?fù)渑判?/h2>

判斷有向無環(huán)圖是進(jìn)行拓?fù)渑判虻牡谝徊?#xff0c;注意只有確定一個圖是一個有向無環(huán)圖,我們才有可能對它進(jìn)行拓?fù)渑判虺晒ΑM負(fù)渑判蛘f到底就是我們要將所有的點排列得滿足每一條邊的規(guī)則,邊的發(fā)出點必須在邊的接受點的前面,所以根據(jù)這個原則我們可以像下面這樣來思考拓?fù)渑判虻膯栴}:
找出排在第一或是最后的點,如果是排在第一的點就說明沒有邊指向它,也就是它的入度為0,如果是最后一個點,說明它沒有邊指向任何一個其他的點,那么它的出度就應(yīng)該為0;這兩種方式在本質(zhì)上是一樣的,為了方便,我們這里以找最后
一個點為例,
? 如果找到了最后一個點,我們就將它存儲在一個棧中,棧的大小為圖的頂點數(shù)。
找到最后一個點之后,我們將這個點刪除,因為在后面的拓?fù)渑判蛑羞@個點已經(jīng)不起作用,其他所有的頂點都可以在它的前面(左邊),所以我們刪除這個頂點及所有指向它的邊,不僅可以縮小圖的規(guī)模還不影響排序的結(jié)果。
? 重復(fù)執(zhí)行步驟2,直到圖中沒有頂點。
? 將棧中的點依次出棧,完成拓?fù)渑判颉?br /> 從上面的算法描述可以看出,拓?fù)渑判蚓哂泻軓?qiáng)的遞歸性質(zhì),其具體執(zhí)行的方法與我們前面所描述的判定有向無環(huán)圖的算法類似。這是理所當(dāng)然的,因為有向無環(huán)圖的判定就是拓?fù)渑判虻幕A(chǔ)和前提。所以我們可以試著對前面判定有向無環(huán)圖的算法進(jìn)行修改,就可以得到拓?fù)渑判虻乃惴?#xff08;這里我們的圖使用鄰接鏈表表示的,用鄰接矩陣結(jié)構(gòu)的情況類似)

void Graphic::topologySort(Graphic *graphic){ int flag = 1; //圖中無環(huán)的標(biāo)志 stack<int> s; //設(shè)置存儲拓?fù)渑判蚪Y(jié)果的棧 queue<int> q; //存儲出度為0 的頂點的隊列 int vertexes = graphic->getVertexNum(); //獲取圖的頂點數(shù) int *outDegree = new int[vertexes]; //存儲各頂點的出度 for (int i = 0;i< vertexes;i++) { outDegree[i] = 0; } list<int>::iterator iters; for (int j = 0;j<vertexes;j++) { int outEdgeNumber = 0; for (iters = graphic->adjacents[j].begin();iters != graphic-> adjacents[j].end();iters++) { outEdgeNumber++; //計算圖中各頂點的出度 } outDegree[j] = outEdgeNumber; } for (int i = 0;i < vertexes; i++) { if (outDegree[i] == 0) { q.push(i); //將出度為0 的頂點入隊 } } for (int v = 0;v < vertexes;v++) { if (q.empty()) //如果隊列為空則圖中有環(huán) { flag = 0; break; } int vertex = q.front(); //隊列中首元素出隊 s.push(vertex); //將最后元素入棧 q.pop(); outDegree[vertex] = -1; //標(biāo)志該頂點出度為-1,表示已訪問過for (int i = 0;i<vertexes;i++) { for (iters = graphic->adjacents[i].begin();iters != graphic-> adjacents[i].end();iters++) { if (*iters == vertex) { outDegree[*iters]--;//將所有有邊指向該結(jié)點的頂點的出度減1 if (outDegree[*iters]==0) { q.push(*iters);//如果有新的出度為0 的點,則將該點入隊 } } } } } if (flag) { while(!s.empty()) { cout<<s.top()<<" "; //輸出棧內(nèi)元素,得到拓?fù)渑判蚪Y(jié)果 s.pop(); } }else{ cout<<"該圖有環(huán)不能進(jìn)行拓?fù)渑判?#xff01;"<<endl; } }

【(注:這是原答案的代碼,但我覺得有問題,outDegree[*iters]–;//將所有有邊指向該結(jié)點的頂點的出度減1
if (outDegree[*iters]==0)
{
q.push(*iters);//如果有新的出度為0 的點,則將該點入隊
}這句話中我覺得并不是q.push(*iters);而是q.push(i)
)】
從上面的程序可以看出,拓?fù)渑判虻臅r間復(fù)雜度其實就是一次圖的遍歷的時間復(fù)雜度,如果圖是鄰接矩陣表示的話,復(fù)雜度為O(v2),如果圖使用鄰接表結(jié)構(gòu)的話,其時間復(fù)雜度則為O(V+E),V 表示定點數(shù),E 表示鄰接鏈表的最大長度

用DFS(鄰接矩陣) 來實現(xiàn)拓?fù)渑判颉?/h2>

問:圖的拓?fù)渑判蚓哂卸喾N結(jié)果,其實現(xiàn)方法也有多種,請利用圖的深度遍歷算法DFS 來實現(xiàn)拓?fù)渑判颉?br /> DFS 算法從起始頂點開始優(yōu)先找到圖中最深的點,直到不能再深入為止,然后離開該點尋找下一個最深點。這句話我們換個說法就是,DFS 會沿著有向邊一直走,直到某個結(jié)點沒有指向別的頂點的邊的點為止。沒有指向別的頂點的邊的點就是出度為0 的點,這正是我們拓?fù)渑判蛞獙ふ业淖詈簏c。這樣我們就將DFS 算法與拓?fù)渑判蛩惴ňo密聯(lián)系起來了
寫法一:

/*************************利用DFS 進(jìn)行拓?fù)渑判?************/ const int maxVertex = 20; bool adj[maxVertex][maxVertex]; //邊的鄰接矩陣 int visited[maxVertex]; //記錄DFS 遍歷過的點 int sorted[maxVertex]; //記錄拓?fù)渑判虻捻樞?/span> int n; bool cycle = false; //DFS 過程中是否有環(huán) void Dfs(int v) {if(visited[v] == 1) cycle = true;//如果v已經(jīng)訪問過,說明進(jìn)行了二次訪問,圖中有環(huán) if(visited[v] == 2) return;//如果visited[v]為2,說明此頂點沒有出度,是拓?fù)渑判虻慕K點 visited[v] = 1; for (int i=0; i<9; i++) if (adj[v][i]) Dfs(i); visited[v] =2; sorted[n--] = v;//記錄排序后的頂點順序 }void topologyByDFS() { //初始化 for (int i=0; i<maxVertex; i++) visited[i] = 0; cycle = false; n = maxVertex-1; //進(jìn)行DFS for (int s=0; s<9; ++s) if (visited[s] == 0) DFS(s); //輸出排序結(jié)果 if (cycle) cout << "圖上有環(huán)"; else{ for (int i=0; i<9; ++i) cout << sorted[i]<<" "; } }

寫法二:

typedef struct anode {int adjvex;//該邊的鄰接點編號struct anode* nexarc;//指向下一條邊的指針int weight;//該邊的相關(guān)信息,比如權(quán)值 }arcnode;//邊結(jié)點類型 typedef struct vnode {//InfoTyoe info; 頂點的其他信息arcnode* firstarc;//指向第一個邊結(jié)點 }Vnode;//鄰接表頭結(jié)點類型 typedef struct {vnode adjlist[10000];//鄰接表頭結(jié)點數(shù)組int n, e;//圖中頂點數(shù)n和邊數(shù)e }adjgraph;//完整的圖鄰接表類型 int visited[maxn]; vector<int>seq;//存放逆拓?fù)湫蛄?/span> void Dfs(adjgraph*g,int u)//從頂點u除非深度優(yōu)先遍歷 { visited[u] = 1;arcnode*p,int w; p = g->adjlist[u].firstarc;//p指向u的第一個鄰接點 while(p!=NULL) { if(!visited[p->adjvex]) //沒有訪問過,從這個點開始深度搜索Dfs(g,p->adjvex];//從這里遞歸p = p->nextarc;//指向下一個鄰接點 } seq.push_back(u);//將u放入序列中 } void TopSort(adjgraph* g) { for(int i = 0;i < g->n;i++) { //輸出有向無環(huán)圖的拓?fù)湫蛄?/span> if(visited[i]) continue; Dfs(g,i);//開始遞歸} cout<<"拓?fù)湫蛄?#xff1a;"; for(int i = seq.size()-1;i>=0;i--) cout<<seq[i];//第一個放入序列的是拓?fù)渑判虻淖詈笠粋€點,所以逆序輸出 }

判斷無向圖頂點是否全部連通

class Graphic{ public: Graphic(int vertex){ this->vertexNum = vertex; adjacents = new list<int>[this->vertexNum]; //構(gòu)造圖 } int getVertexNum(){return this->vertexNum;//獲得頂點數(shù) } void setVertexNum(int number) {//修改頂點數(shù)this->vertexNum = number; } void countOutdegree(Graphic*g, int *outdegree);//計算所有頂點的出度 bool isDAG(Graphic *g);//判斷是否為有向無環(huán)圖 bool deleteVertex(int v) {list<int>*p = &adjacents[v];//取出該list的頂點v的地址,獲取指定點的出度鏈表p->clear();delete p;//刪除指定的頂點及其出度邊//需對原來的圖結(jié)構(gòu)進(jìn)行修改for(int i = v;i < vertexNum -1;i++)adjacents[v] = adjacents[v+1];//相當(dāng)于全部元素向前移,因為刪除了一個this->vertexNum--;//頂點數(shù)也要修改 list<int>* newadj = new list[this->vertexNum]; for(int i = 0;i < this->vertexNum;i++) newadj[i] = adjacents[i];//將所有的原list中的元素賦值到一個臨時new的list中 list<int>*q = newadj; this->adjacents = newadj; delete q;//刪除q } private: int vertexNum; //頂點數(shù) list<int> *adjacents; //鄰接表存儲邊關(guān)系 注意它的類型,是一個list類型中存儲了很多個指針 }; void Graphic::countOutdegree(Graphic *g, int *indegree){int total = g->getVertexNum(); int v; list<int>::iterator it; for(v = 0;v < total;v++) {int number = 0;for(it =adjacents[v].begin(); it != adjacents[v].end() ;it++){number++;}indegree[v] = number; } } bool Graphic::isDAG(Graphic *graphic){int total = graphic->getVertexNum();int* outdegree = new int[total];for(int i = 0;i < total;i++)outdegree[i] = 0;//初始化數(shù)組Graphic::countOutdegree(graphic,outdegree); bool ifHas = false;//標(biāo)志,判斷是否存在出度為0的點 list<int>zeroOutDegree;//存儲所有出度為0的點 for(int i = 0;i < total;i++) {if(outdegree[i] == 0){zeroOutDegree.push_back(outdegree[i]);ifHas = true;} } if(ifHas)//如果有出度為0的邊 { graphic->deleteVertex(zeroOutDegree.front()); if(graphic->getVertexNum() > 0) {isDAG(graphic);//遞歸入口 } else return true;//遞歸判斷該圖 每次都刪除出度為0的點和出度邊 } else return false;//如果ifHas為False表示沒有出度為0的點 }

判斷圖G中從頂點u到v是否存在簡單路徑

假設(shè)圖G采用鄰接表存儲,設(shè)計算法判斷圖G中從頂點u到v是否存在簡單路徑(深度優(yōu)先遍歷的方法,如果遍歷到v說明存在簡單路徑)

關(guān)鍵:
1.遍歷所有結(jié)點
2.要注意標(biāo)記是否已經(jīng)遍歷過
3.遞歸調(diào)用,當(dāng)從u->v經(jīng)過w時,改u為w,繼續(xù)遞歸調(diào)用,若w==v,說明存在從u出發(fā)經(jīng)過v的環(huán)
4.先是判斷對于某一個結(jié)點,是否存在從它出發(fā)的環(huán),然后判斷整個圖
5.注意在判斷圖G時,從i=0開始,cycle(G,v,v,-1);即從v到v且路徑長度大于1

//在用鄰接表來表示圖時,圖中的每個頂點v都對應(yīng)一個單鏈表,每個單鏈表有一個頭結(jié)點 所有這些頭結(jié)點構(gòu)成頭結(jié)點數(shù)組 typedef struct anode {int adjvex;//該邊的鄰接點編號struct anode* nexarc;//指向下一條邊的指針int weight;//該邊的相關(guān)信息,比如權(quán)值 }arcnode;//邊結(jié)點類型 typedef struct vnode {//InfoTyoe info; 頂點的其他信息arcnode* firstarc;//指向第一個邊結(jié)點 }Vnode;//鄰接表頭結(jié)點類型 typedef struct {vnode adjlist[10000];//鄰接表頭結(jié)點數(shù)組int n, e;//圖中頂點數(shù)n和邊數(shù)e }adjgraph;//完整的圖鄰接表類型bool visited[maxn]={false}; bool exitpath(adjgraph*G,int u,int v) { bool flag; int w; arcnode* p; visited[u] = 1;//設(shè)置為已經(jīng)訪問 if(u == v) return true;//找到了一條路徑 p = G->adjlist[u].firstarc;//p指向頂點u的第一個鄰接點 while(p != NULL) { w = p->adjvex; if(!visited(w)) {//如果頂點w未被訪問,遞歸訪問它 flag = exitpath(G,w,v); if(flag) return true; } p = p->nexarc;//p指向頂點u的下一個鄰接點 } }

輸出圖G中從頂點u到v的所有簡單路徑

假設(shè)圖G采用鄰接表存儲,設(shè)計算法輸出圖G中從頂點u到v的所有簡單路徑
利用回溯的深度優(yōu)先搜索,在遍歷過程中每個頂點只訪問一次,所以這條路徑一定是一條簡單路徑。當(dāng)從頂點u出發(fā)遍歷時先將visited[u]置1,加入到path向量中,找到一條路徑后從終點v回退繼續(xù)尋找其他路徑,允許曾經(jīng)訪問過的頂點出現(xiàn)在另外的路徑中

bool visted[maxn]={false}; void findpath(adjgraph*G,int u,int v,vector<int>path) { int w,i; arcnode* p; visited[u]=true;//設(shè)置為已經(jīng)訪問 path.push_back(u); if(u == v && path.size()>0) { for(i = 0;i < path.size();i++)//輸出一條路徑 cout<<path[i]; cout<<"\n"; } p = G->adjlist[u].firstarc;//p指向u的第一個鄰接點 while(p!= NULL) { w = p->adjvex; if(visited[w]==false) { findpath(G,w,v,path); } p = p->nextarc; } visited[u]=false;//恢復(fù)環(huán)境,使得u可用 }

如果要求 輸出從u到v長度為m的路徑,則增加判斷path.size==m+1

判斷無向圖是否存在經(jīng)過頂點v的環(huán)

從v出發(fā)深度優(yōu)先搜索,用d記錄經(jīng)過的路徑長度(初始值d=-1),如果搜索到頂點v且d>1表示從v到v有一個長度大于1的路徑

bool visited[maxn]={false}; bool cycle(adjgraph*G,int u,int v,int d) { visited[u]=true; bool flag; arcnode*p; p=G->adjlist[u].firstarc; d++; int w; while(p!=NULL) { w = p->adjvex; if(visited[w]==false)//如果頂點沒有被訪問,遞歸訪問它 { flag = cycle(G,w,v,d);//從頂點w出發(fā)搜索 if(flag)return true; } else if(w == v && d>1)//搜索到頂點v且環(huán)的長度大于1 return true; p = p->nextarc; } return false; }bool hascycle(adjgraph*G,int v) { return cycle(G,v,v,-1);//從頂點v出發(fā)搜索 }

若改為:

輸出所有無向圖中經(jīng)過頂點v的環(huán)

v出發(fā)深度優(yōu)先搜索,用path向量記錄經(jīng)過的路徑長度,如搜到一條路徑,將path添加到allpath中,將visited[u]恢復(fù)為false
最后輸出allpath中所有的環(huán)

bool visited[maxn]={false}; vector<vector<int>>&allpath;//全局向量的向量 存放所有環(huán) void allcycle(adjgraph* G,int u,int v,vector<int>path) {int w; arcnode* p; visited[u]=true; path.push_back(u); p = G->adjlist[u].firstarc; while(p!=NULL) { w = p->adjvex; if(visited[w]==false )//若w未被訪問 ,遞歸訪問它 從w出發(fā)搜索 { allcycle(G,w,v,path); } else if(w ==v && path.size()>2) allpath.push_back(path);//從v到v之間找到一個環(huán)且長度大于2 p = p->nextadj; } visited[u]=false; }void displaypath(adjgraph*G,int v) { vector<int>path; vector<vector<int>>allpath; allcycle(G,v,v,path); for(int i = 0;i < allpath.size();i++) { for(int j = 0;j < allpath[i].size();j++) cout<<allpath[i][j]; cout<<"\n"; }

總結(jié)

以上是生活随笔為你收集整理的【算法】图(一)拓扑排序的实现 图的邻接表算法 判断是否图G中存在环的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。