日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

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

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

文章目錄

    • 用list來表示圖,判斷是否存在環
    • 鄰接表實現拓撲排序
    • 用DFS(鄰接矩陣) 來實現拓撲排序。
    • 判斷無向圖頂點是否全部連通
    • 判斷圖G中從頂點u到v是否存在簡單路徑
    • 輸出圖G中從頂點u到v的所有簡單路徑
    • 判斷無向圖是否存在經過頂點v的環
      • 輸出所有無向圖中經過頂點v的環

用list來表示圖,判斷是否存在環

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

class Graphic{ public: Graphic(int vertex){ this->vertexNum = vertex; adjacents = new list<int>[this->vertexNum]; //構造圖 } int getVertexNum(){return this->vertexNum;//獲得頂點數 } void setVertexNum(int number) {//修改頂點數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);//判斷是否為有向無環圖 bool deleteVertex(int v) {list<int>*p = &adjacents[v];//取出該list的頂點v的地址,獲取指定點的出度鏈表p->clear();delete p;//刪除指定的頂點及其出度邊//需對原來的圖結構進行修改for(int i = v;i < vertexNum -1;i++)adjacents[v] = adjacents[v+1];//相當于全部元素向前移,因為刪除了一個this->vertexNum--;//頂點數也要修改 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; //頂點數 list<int> *adjacents; //鄰接表存儲邊關系 注意它的類型,是一個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;//初始化數組Graphic::countOutdegree(graphic,outdegree); bool ifHas = false;//標志,判斷是否存在出度為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的點 }

鄰接表實現拓撲排序

判斷有向無環圖是進行拓撲排序的第一步,注意只有確定一個圖是一個有向無環圖,我們才有可能對它進行拓撲排序成功。拓撲排序說到底就是我們要將所有的點排列得滿足每一條邊的規則,邊的發出點必須在邊的接受點的前面,所以根據這個原則我們可以像下面這樣來思考拓撲排序的問題:
找出排在第一或是最后的點,如果是排在第一的點就說明沒有邊指向它,也就是它的入度為0,如果是最后一個點,說明它沒有邊指向任何一個其他的點,那么它的出度就應該為0;這兩種方式在本質上是一樣的,為了方便,我們這里以找最后
一個點為例,
? 如果找到了最后一個點,我們就將它存儲在一個棧中,棧的大小為圖的頂點數。
找到最后一個點之后,我們將這個點刪除,因為在后面的拓撲排序中這個點已經不起作用,其他所有的頂點都可以在它的前面(左邊),所以我們刪除這個頂點及所有指向它的邊,不僅可以縮小圖的規模還不影響排序的結果。
? 重復執行步驟2,直到圖中沒有頂點。
? 將棧中的點依次出棧,完成拓撲排序。
從上面的算法描述可以看出,拓撲排序具有很強的遞歸性質,其具體執行的方法與我們前面所描述的判定有向無環圖的算法類似。這是理所當然的,因為有向無環圖的判定就是拓撲排序的基礎和前提。所以我們可以試著對前面判定有向無環圖的算法進行修改,就可以得到拓撲排序的算法(這里我們的圖使用鄰接鏈表表示的,用鄰接矩陣結構的情況類似)

void Graphic::topologySort(Graphic *graphic){ int flag = 1; //圖中無環的標志 stack<int> s; //設置存儲拓撲排序結果的棧 queue<int> q; //存儲出度為0 的頂點的隊列 int vertexes = graphic->getVertexNum(); //獲取圖的頂點數 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()) //如果隊列為空則圖中有環 { flag = 0; break; } int vertex = q.front(); //隊列中首元素出隊 s.push(vertex); //將最后元素入棧 q.pop(); outDegree[vertex] = -1; //標志該頂點出度為-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]--;//將所有有邊指向該結點的頂點的出度減1 if (outDegree[*iters]==0) { q.push(*iters);//如果有新的出度為0 的點,則將該點入隊 } } } } } if (flag) { while(!s.empty()) { cout<<s.top()<<" "; //輸出棧內元素,得到拓撲排序結果 s.pop(); } }else{ cout<<"該圖有環不能進行拓撲排序!"<<endl; } }

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

用DFS(鄰接矩陣) 來實現拓撲排序。

問:圖的拓撲排序具有多種結果,其實現方法也有多種,請利用圖的深度遍歷算法DFS 來實現拓撲排序。
DFS 算法從起始頂點開始優先找到圖中最深的點,直到不能再深入為止,然后離開該點尋找下一個最深點。這句話我們換個說法就是,DFS 會沿著有向邊一直走,直到某個結點沒有指向別的頂點的邊的點為止。沒有指向別的頂點的邊的點就是出度為0 的點,這正是我們拓撲排序要尋找的最后點。這樣我們就將DFS 算法與拓撲排序算法緊密聯系起來了
寫法一:

/*************************利用DFS 進行拓撲排序*************/ const int maxVertex = 20; bool adj[maxVertex][maxVertex]; //邊的鄰接矩陣 int visited[maxVertex]; //記錄DFS 遍歷過的點 int sorted[maxVertex]; //記錄拓撲排序的順序 int n; bool cycle = false; //DFS 過程中是否有環 void Dfs(int v) {if(visited[v] == 1) cycle = true;//如果v已經訪問過,說明進行了二次訪問,圖中有環 if(visited[v] == 2) return;//如果visited[v]為2,說明此頂點沒有出度,是拓撲排序的終點 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; //進行DFS for (int s=0; s<9; ++s) if (visited[s] == 0) DFS(s); //輸出排序結果 if (cycle) cout << "圖上有環"; else{ for (int i=0; i<9; ++i) cout << sorted[i]<<" "; } }

寫法二:

typedef struct anode {int adjvex;//該邊的鄰接點編號struct anode* nexarc;//指向下一條邊的指針int weight;//該邊的相關信息,比如權值 }arcnode;//邊結點類型 typedef struct vnode {//InfoTyoe info; 頂點的其他信息arcnode* firstarc;//指向第一個邊結點 }Vnode;//鄰接表頭結點類型 typedef struct {vnode adjlist[10000];//鄰接表頭結點數組int n, e;//圖中頂點數n和邊數e }adjgraph;//完整的圖鄰接表類型 int visited[maxn]; vector<int>seq;//存放逆拓撲序列 void Dfs(adjgraph*g,int u)//從頂點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++) { //輸出有向無環圖的拓撲序列 if(visited[i]) continue; Dfs(g,i);//開始遞歸} cout<<"拓撲序列:"; for(int i = seq.size()-1;i>=0;i--) cout<<seq[i];//第一個放入序列的是拓撲排序的最后一個點,所以逆序輸出 }

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

class Graphic{ public: Graphic(int vertex){ this->vertexNum = vertex; adjacents = new list<int>[this->vertexNum]; //構造圖 } int getVertexNum(){return this->vertexNum;//獲得頂點數 } void setVertexNum(int number) {//修改頂點數this->vertexNum = number; } void countOutdegree(Graphic*g, int *outdegree);//計算所有頂點的出度 bool isDAG(Graphic *g);//判斷是否為有向無環圖 bool deleteVertex(int v) {list<int>*p = &adjacents[v];//取出該list的頂點v的地址,獲取指定點的出度鏈表p->clear();delete p;//刪除指定的頂點及其出度邊//需對原來的圖結構進行修改for(int i = v;i < vertexNum -1;i++)adjacents[v] = adjacents[v+1];//相當于全部元素向前移,因為刪除了一個this->vertexNum--;//頂點數也要修改 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; //頂點數 list<int> *adjacents; //鄰接表存儲邊關系 注意它的類型,是一個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;//初始化數組Graphic::countOutdegree(graphic,outdegree); bool ifHas = false;//標志,判斷是否存在出度為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是否存在簡單路徑

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

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

//在用鄰接表來表示圖時,圖中的每個頂點v都對應一個單鏈表,每個單鏈表有一個頭結點 所有這些頭結點構成頭結點數組 typedef struct anode {int adjvex;//該邊的鄰接點編號struct anode* nexarc;//指向下一條邊的指針int weight;//該邊的相關信息,比如權值 }arcnode;//邊結點類型 typedef struct vnode {//InfoTyoe info; 頂點的其他信息arcnode* firstarc;//指向第一個邊結點 }Vnode;//鄰接表頭結點類型 typedef struct {vnode adjlist[10000];//鄰接表頭結點數組int n, e;//圖中頂點數n和邊數e }adjgraph;//完整的圖鄰接表類型bool visited[maxn]={false}; bool exitpath(adjgraph*G,int u,int v) { bool flag; int w; arcnode* p; visited[u] = 1;//設置為已經訪問 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的所有簡單路徑

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

bool visted[maxn]={false}; void findpath(adjgraph*G,int u,int v,vector<int>path) { int w,i; arcnode* p; visited[u]=true;//設置為已經訪問 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;//恢復環境,使得u可用 }

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

判斷無向圖是否存在經過頂點v的環

從v出發深度優先搜索,用d記錄經過的路徑長度(初始值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出發搜索 if(flag)return true; } else if(w == v && d>1)//搜索到頂點v且環的長度大于1 return true; p = p->nextarc; } return false; }bool hascycle(adjgraph*G,int v) { return cycle(G,v,v,-1);//從頂點v出發搜索 }

若改為:

輸出所有無向圖中經過頂點v的環

v出發深度優先搜索,用path向量記錄經過的路徑長度,如搜到一條路徑,將path添加到allpath中,將visited[u]恢復為false
最后輸出allpath中所有的環

bool visited[maxn]={false}; vector<vector<int>>&allpath;//全局向量的向量 存放所有環 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出發搜索 { allcycle(G,w,v,path); } else if(w ==v && path.size()>2) allpath.push_back(path);//從v到v之間找到一個環且長度大于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"; }

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。