日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > [置頂] 圖論(一):DFS,BFS,鄰接鏈表,并查集

[置頂] 圖論(一):DFS,BFS,鄰接鏈表,并查集

來源:程序員人生   發布時間:2016-06-17 08:44:57 閱讀次數:2425次

本文總結了圖的深度優先搜索,圖的廣度優先搜索,鄰接鏈表和鄰接矩陣的實現,并查集的實現。

0),豫備知識
       
基礎辭匯:有向圖,無向圖,帶權有向圖,帶權無向圖,有向圖中<Vi, Vj>:即Vi--->Vj,弧尾--->弧頭,無向圖中相鄰記為(Vi, Vj),頂點有窮集合V+邊的有窮集合E。
        圖的兩種實現方式:1,鄰接矩陣:edge[n][n]表示有n個結點,數組內容為權值大小或是不是存在邊(∞表示無邊,權值或1表示有邊,0表示結點到結點本身);
                                2,鄰接鏈表:針對稀疏矩陣較適合,為圖的每一個頂點都建立1個單鏈表,第i個單鏈表中保存與結點Vi所有相鄰結點信息(無向圖)或弧尾結點信息(有向圖),和邊信息。


1),圖的深度優先搜索(DFS)
        深度優先算法的主要思想是:首先以1個未被訪問過的頂點作為起始頂點,沿當前頂點的邊走到未訪問過的頂點;當沒有未訪問過的頂點時,則回到上1個頂點,繼續摸索訪問別的頂點,直到所有的頂點都被訪問過。明顯,深度優先遍歷是沿著圖的某1條分支遍歷直到末端,然后回溯,再沿著另外一條進行一樣的遍歷。
        例1:以下圖為例,從結點1開始DFS,程序的算法思想是:在主函數中初始化連接矩陣,調用dfs(1) ---> 設置變量cnt,將cnt等于結點個數作為dfs遞歸的臨界條件 ---> 設置標志mark[n],鐺鐺前訪問結點的邊指向的下1結點未被訪問時,進行dfs調度訪問(結點的相鄰結點的訪問順序為標號由小到大的順序)


/***先輸入n個結點,m條邊,以后輸入無向圖的m條邊,以后對上圖輸出DFS遍歷的結點順序***/ #include <iostream> #include <iomanip> #define nmax 110 #define inf 999999999 using namespace std; int n, m, cnt, edge[nmax][nmax], mark[nmax];//結點數,邊數,計數值,鄰接矩陣,結點訪問標記 void dfs(int cur){ cnt++; /***operation***/ if(cnt == 1) cout << cur; else cout << setw(3) << cur; /***operation***/ if(cnt == n) return; else{ int i; for(i = 1; i <= n; i++){ if(edge[cur][i] == 1 && mark[i] == 0){ mark[i] = 1; dfs(i); } } return; } } int main(){ while(cin >> n >> m && n != 0){ //初始化鄰接矩陣 int i, j; for(i = 1; i <= n; i++){ for(j = 1; j <= n; j++){ edge[i][j] = inf; } edge[i][i] = 0; } int a, b; while(m--){ cin >> a >> b; edge[a][b] = edge[b][a] = 1; } //以dnf(1)為出發點開始遞歸遍歷 memset(mark, 0, sizeof(mark)); cnt = 0; mark[1] = 1; dfs(1); cout << endl; } return 0; }
        程序運行結果:

        例2:下面是城市的地圖,注意是單向圖,求城市1到城市5的最短距離
        程序思路:從1號城市動身,可到達2號城市和5號城市,若依照1到n的順序則先訪問2號城市;訪問完2號城市后,由于2號城市可到達的城市有3號和5號城市,我們訪問3號城市;爾后,3號城市可到達1號,4號城市,由于1號城市已被訪問過,我們訪問4號城市;4號城市又可到達5號城市,我們最后訪問5號城市。但是,1->2->3->4->5其實不1定是最短路徑,我們需要撤除5號城市的訪問標記,返回到4號城市,由于經過4號城市已訪問過5號城市而又沒有其他城市可訪問;再返回到3號城市,經過3號城市訪問過4號城市,則看看能否訪問5號城市,不能則再返回到2號城市,這時候2號城市有路徑直達5號城市,即1->2->5.....如此折回再前進,直至找到所有1號城市能到達5號城市的路徑,取其中的最小值。


/***先輸入n個結點,m條邊,以后輸入有向圖的m條邊,邊的前兩元素表示起始結點,第3個值表權值,輸出1號城市到n號城市的最短距離***/ /***算法的思路是訪問所有的深度遍歷路徑,需要在深度遍歷返回時將訪問標志置0***/ #include <iostream> #include <iomanip> #define nmax 110 #define inf 999999999 using namespace std; int n, m, minPath, edge[nmax][nmax], mark[nmax];//結點數,邊數,最小路徑,鄰接矩陣,結點訪問標記 void dfs(int cur, int dst){ /***operation***/ /***operation***/ if(minPath < dst) return;//當前走過路徑大于之前最短路徑,沒必要再走下去 if(cur == n){//臨界條件 if(minPath > dst) minPath = dst; return; } else{ int i; for(i = 1; i <= n; i++){ if(edge[cur][i] != inf && edge[cur][i] != 0 && mark[i] == 0){ mark[i] = 1; dfs(i, dst+edge[cur][i]); mark[i] = 0; } } return; } } int main(){ while(cin >> n >> m && n != 0){ //初始化鄰接矩陣 int i, j; for(i = 1; i <= n; i++){ for(j = 1; j <= n; j++){ edge[i][j] = inf; } edge[i][i] = 0; } int a, b; while(m--){ cin >> a >> b; cin >> edge[a][b]; } //以dnf(1)為出發點開始遞歸遍歷 memset(mark, 0, sizeof(mark)); minPath = inf; mark[1] = 1; dfs(1, 0); cout << minPath << endl; } return 0; }

        程序運行結果以下:



2),圖的廣度優先搜索(BFS)
          廣度優先遍歷的主要思想是:首先以1個未被訪問過的頂點作為起始頂點,訪問其所有相鄰的頂點,然后對每一個相鄰的頂點,再訪問它們相鄰的未被訪問過的頂點,直到所有頂點都被訪問過,遍歷結束。

        例1:根據1)例1中的無向圖,輸出其廣度優先搜索順序。
        程序主要思想:結點1入隊 ---> while根據隊列非空進行循環 ---> 出隊并將相鄰結點入隊,另外需要設置計數值cnt,每次出隊時加1。

/***先輸入n個結點,m條邊,以后輸入無向圖的m條邊,邊的兩元素表示起始結點***/ #include <iostream> #include <queue> #include <iomanip> using namespace std; #define nmax 110 #define inf 999999999 queue<int> que; int n, m, mark[nmax], edge[nmax][nmax], cnt; int main(){ while(cin >> n >> m && n != 0){ int i, j; //初始化鄰接鏈表 for(i = 1; i <= n; i++){ for(j = 1; j <= n; j++){ edge[i][j] = inf; } edge[i][i] = 0; } int a, b; while(m--){ cin >> a >> b; edge[a][b] = edge[b][a] = 1; } while(!que.empty()) que.pop(); memset(mark, 0, sizeof(mark)); //開始廣度優先遍歷 int head; cnt = 0; mark[1] = 1; que.push(1); while(!que.empty()){ head = que.front(); que.pop(); /***operation***/ cnt++; if(cnt == 1) cout << head; else cout << setw(3) << head; /***operation***/ for(i = 1; i <= n; i++){ if(edge[head][i] == 1 && mark[i] == 0){ mark[i] = 1; que.push(i); } } } cout << endl; if(cnt != n) cout << "The original image is not connected.\n"; } return 0; }
        程序運行結果以下:



        例2:以下圖,需要坐飛機從1號城市到5號城市,求最小的轉機次數


/***先輸入n個結點,m條邊,動身城市,終點城市,以后輸入無向圖的m條邊,邊的兩元素表示起始結點***/ /***需要構建結點結構體Node,寄存結點編號和遍歷層數,每次for循環時該層數在上1層基礎上加1***/ #include <iostream> #include <queue> using namespace std; #define nmax 110 #define inf 999999999 struct Node{ int node; int cnt; }; queue<Node> que; int m, n, edge[nmax][nmax], mark[nmax], startNum, endNum; int main(){ while(cin >> n >> m >> startNum >> endNum && n != 0){ bool tag = false; //初始化鄰接矩陣 int i, j; for(i = 1; i <= n; i++){ for(j = 1; j <= n; j++){ edge[i][j] = inf; } edge[i][i] = 0; } while(m--){ cin >> i >> j; edge[i][j] = edge[j][i] = 1; } //開始廣度優先遍歷 memset(mark, 0, sizeof(mark)); while(!que.empty()) que.pop(); Node tmp; tmp.node = startNum; tmp.cnt = 0; que.push(tmp); mark[tmp.node] = 1; while(!que.empty()){ Node head = que.front(); que.pop(); Node temp; for(i = 1; i <= n; i++){ if(edge[head.node][i] == 1 && mark[i] == 0){ temp.node = i; temp.cnt = head.cnt + 1; que.push(temp);//注意寫在if語句里面 mark[temp.node] = 1; if(temp.node == endNum){ tag = true; cout << temp.cnt << endl; break; } } } if(tag) break; } } return 0; }
        程序運行結果以下:




3),鄰接鏈表和鄰接矩陣的實現
       
鄰接表由表頭結點(結點信息)和表結點(邊信息)兩部份組成,其中圖中每一個頂點均對應1個存儲在數組中的表頭結點。以下為帶權有向圖:

        例1:鄰接鏈表中每一個頂點均對應1個存儲在數組中的表頭結點,為簡化,每一個鄰接結點結構體只包括下1相鄰結點編號和邊權,使用標準模板vector編程實現。另外也可由鄰接矩陣實現,上圖實現以下:

/*****了解邊信息結構體和鄰接鏈表的實現,并在實現好基礎上增加刪除1些邊,以后再用鄰接矩陣實現,學習erase,push_back,setw的用法*****/ #include <iostream> #include <iomanip> #include <vector> #define inf ⑴//設置無窮大為⑴,表示無邊 using namespace std; int n; //邊信息,包括連接結點編號和邊的權重 struct Edge{ int adjNodeNum; int edgeWeight; }; struct Node{//結點信息 int nodeNum; char data; }node[110]; vector<Edge> adjList[110]; //鄰接鏈表,該圖最多有110個結點 int adjMatrix[110][110];//鄰接矩陣 void initAdjList(){ int i; for(i = 0; i < n; i++) adjList[i].clear();//清空--->構建--->具體操作 Edge tmp0[2], tmp1[2], tmp2, tmp3[3]; tmp0[0].adjNodeNum = 1; tmp0[0].edgeWeight = 1; tmp0[1].adjNodeNum = 2; tmp0[1].edgeWeight = 4; node[0].data = 'A'; node[0].nodeNum = 0; adjList[0].push_back(tmp0[0]); adjList[0].push_back(tmp0[1]); tmp1[0].adjNodeNum = 2; tmp1[0].edgeWeight = 2; tmp1[1].adjNodeNum = 3; tmp1[1].edgeWeight = 9; node[1].data = 'B'; node[1].nodeNum = 1; adjList[1].push_back(tmp1[0]); adjList[1].push_back(tmp1[1]); tmp2.adjNodeNum = 3; tmp2.edgeWeight = 6; node[2].data = 'D'; node[2].nodeNum = 2; adjList[2].push_back(tmp2); tmp3[0].adjNodeNum = 0; tmp3[0].edgeWeight = 3; tmp3[1].adjNodeNum = 1; tmp3[1].edgeWeight = 5; tmp3[2].adjNodeNum = 2; tmp3[2].edgeWeight = 8; adjList[3].push_back(tmp3[0]); adjList[3].push_back(tmp3[1]); adjList[3].push_back(tmp3[2]); node[3].data = 'C'; node[3].nodeNum = 3; } void output(){ int i, j; for(i = 0; i < n; i++){ cout << node[i].nodeNum << setw(2) << node[i].data; for(j = 0; j < adjList[i].size(); j++){ cout << setw(6) << adjList[i][j].adjNodeNum << setw(2) << adjList[i][j].edgeWeight; } cout << setw(9) << "NULL" << endl; } } void makeChange(){ cout << "make some changes......:\n"; Edge temp; temp.adjNodeNum = 3; temp.edgeWeight = 5; adjList[0].push_back(temp);//鄰接表0中增加1個元素 adjList[3].erase(adjList[3].begin()+1, adjList[3].begin()+3);//鄰接表3中刪除兩個元素 } void initMatrix(){ int i, j; for(i = 0; i < n; i++){ for(j = 0; j < n; j++){ adjMatrix[i][j] = inf; } adjMatrix[i][i] = 0; } adjMatrix[0][1] = 1; adjMatrix[0][2] = 4; adjMatrix[1][2] = 2; adjMatrix[1][3] = 9; adjMatrix[2][3] = 6; adjMatrix[3][0] = 3; adjMatrix[3][1] = 5; adjMatrix[3][2] = 8; } void output1(){ int i, j; cout << "output the adjacency matrix:\n"; for(i = 0; i < n; i++){ for(j = 0; j < n; j++){ if(j == 0) cout << adjMatrix[i][j]; else cout << setw(3) << adjMatrix[i][j]; } cout << endl; } } int main(){ n = 4; initAdjList(); output(); makeChange(); output(); initMatrix(); output1(); return 0; }

        程序運行結果以下:



4),并查集的實現
       
并查集:用1棵樹上的結點來表示在1個集合中的數字,如以下{1,2,3,4}和{5,6},再用每一個結點中的內容表示其雙親結點,如Tree[N],則Tree[1] = ⑴, Tree[2] = 1, Tree[6] = 5......
如果想要合并這兩棵樹棵樹,則可令Tree[5] = ⑴變成Tree[5] = 1。以下:

        對,以下1種特殊情況,需要在查找樹的根節點時,加以1定的束縛與優化,將遍歷過的元素的雙親結點設為根節點,以下:



        例1:并查集的實現

/****實現并查集的數組組成,找根節點,合并兩個集合,緊縮路徑************************/ #include <iostream> #include <iomanip> using namespace std; int Set[110]; int Tree[110]; //使用查找函數來尋覓x所在樹的根節點 int findRoot(int x){ if(Tree[x] == ⑴) return x; else return findRoot(Tree[x]); } //使用緊縮路徑的查找函數來查找x所在樹的根節點,只緊縮x到root所查找的路徑 int findRoot1(int x){ if(Tree[x] == ⑴) return x; else{ int tmp = findRoot1(Tree[x]); Tree[x] = tmp; return tmp;//層層返回 } } //使用非遞歸方式 int findRoot_(int x){ while(Tree[x] != ⑴) x = Tree[x]; return x; } int findRoot_1(int x){ int tmp = x; while(Tree[x] != ⑴) x = Tree[x]; while(Tree[tmp] != ⑴) {tmp = Tree[tmp]; Tree[tmp] = x;}//tmp先保存父節點方便while循環,再對數組內容做改變 return x; } int main(){ int n1, n2;//集合1,2的元素個數 cout << "Please input the number of set1 and the number of set2:\n"; while(cin >> n1 >> n2){ int i = 0, j; //輸入集合1和2的數組表,第1行代表元素,第2行代表元素的雙親結點 cout << "Please input set1(first line is node, second line is its father node):\n"; for(i = 0; i < n1; i++) cin >> Set[i]; for(i = 0; i < n1; i++) cin >> Tree[Set[i]]; cout << "Please input set2(first line is node, second line is its father node):\n"; for(i = n1 ; i < n1 + n2; i++) cin >> Set[i]; for(i = n1 ; i < n1 + n2; i++) cin >> Tree[Set[i]]; //輸入集合1和集合2中的1個元素,找出各自的根節點 cout << "Please input two nodes of the two sets (and output its root node):\n"; int sub1, sub2; cin >> sub1 >> sub2; cout << "It's root node is: " << findRoot(sub1) << " " << findRoot(sub2) << endl; //合并集合1和集合2,并顯示 cout << "Merge two trees:\n"; Tree[findRoot1(sub2)] = findRoot1(sub1); for(i = 0; i < n1 + n2; i++) cout << setw(2) <<Set[i]; cout << endl; for(i = 0; i < n1 + n2; i++) cout << setw(2) << Tree[Set[i]]; cout << endl; cout << "Please input the number of set1 and the number of set2:\n"; } }

        程序運行結果以下:

        例2:暢通工程

/****輸入城鎮數n和道路數m,再輸入每條道路連通的城鎮(城鎮從1開始編號),看最少需再修幾條路使全部城鎮連通;若輸入n且n=0結束輸入***********/ /****初始化n個并查集,爾后每輸入1條道路,就將關聯的兩個城鎮加入同1并查集,最后由獨立的并查集個數⑴即為所求答案*********************/ #include <iostream> using namespace std; int Tree[1100]; int findRoot(int x){ if(Tree[x] == ⑴) return x; else{ int tmp = findRoot(Tree[x]); Tree[x] = tmp; return tmp; } } int main(){ int n, m; while(cin >> n && n!= 0){ cin >> m; int i; for(i = 1; i <= n; i++)//初始化n個并查集 Tree[i] = ⑴; int a, b; for(i = m; i >= 1; i--){//處理m條道路,合并并查集 cin >> a >> b; a = findRoot(a); b = findRoot(b); if(a != b) Tree[b] = a; //此處必須先進行比較,如果a,b相同,則會使根節點不為⑴,致使并查集總數變少 } int count = 0; for(i = 1; i <= n; i++)//計算剩余獨立的并查集 if(Tree[i] == ⑴) ++count; cout << "Need to build the number of roads is: " << count - 1 << endl; } }

        程序運行結果以下:

        例3:More Is Better

/***有1000 0000個小朋友,隨機選擇朋友關系,每次選中兩個人,且朋友關系具有傳遞性,當選擇m次后,輸出最大朋友關系人數或1**********************/ /***思路與暢通工程類似,初始化并查集,對每次選擇進行并查集合并,由于需要計算最大并查集元素個數,需要初始化每一個sum[i]=1,每次合并都進行累加便可**/ /***先輸入關系次數m,再順次輸入這m組關系****/ #include <iostream> using namespace std; #define num 10000001 int Tree[num]; int Sum[num]; int findRoot(int x){ if(Tree[x] == ⑴) return x; else{ int tmp = findRoot(Tree[x]); Tree[x] = tmp; return tmp; } } int main(){ int m; while(cin >> m && m != 0){ int i; for(i = 1; i < num; i++) {Tree[i] = ⑴; Sum[i] = 1;} int a, b; while(m--){ cin >> a >> b; a = findRoot(a); b = findRoot(b); if(a != b) {Tree[b] = a; Sum[a] += Sum[b];} } int max = 1; for(i = 1; i < num; i++) if(Sum[i] > max) max = Sum[i]; cout << "Maximum number of friends is: " << max << endl; } return 0; }

        程序運行結果以下:



生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 欧美日韩免费看 | 欧美精品久久久久a | 青青草这里只有精品 | 日韩成人美女视频 | 久久久久一区二区三区四区 | 国产成人精品一区二区三区视频 | 日韩hd| 色网在线 | 亚洲最大福利网站 | www.色五月| 国产精品成人一区二区三区 | 国产乱码精品1区2区3区 | 久久久久久久久久久久91 | 国产欧美欧洲 | 性爱免费视频 | 在线视频精品一区 | 亚洲激情一区 | 天天射天天射天天射 | 日本不卡免费新一二三区 | 国产福利91| 精品自拍视频 | 国产午夜在线 | 久久精品午夜 | 免费精品一区 | 欧美日韩国产中文 | 欧美激情专区 | 精品国产凹凸成av人导航 | 国产激情网站 | 国产午夜在线 | 国产农村乱色xxxx | 91视频你懂的 | 91午夜视频 | 亚洲视频在线一区二区 | 国产乱码精品一区二区三区五月婷 | 亚洲黄色毛片 | 欧美精选一区 | 精品中文字幕一区二区三区 | 狠狠操狠狠干 | 日本久草| 免费的黄色网址 | 色综合二区 |