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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

noj数据结构稀疏矩阵的加法十字链表_数据结构之:图

發布時間:2025/3/11 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 noj数据结构稀疏矩阵的加法十字链表_数据结构之:图 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

導讀

1. 什么是圖?圖的存儲方式?

2. 圖的遍歷(深度優先搜索,廣度優先搜索)

3. 最短路徑

1. 什么是圖?圖的存儲方式?

  前面總結了“樹”這種數據結構,而這篇博客總結的是更為復雜的一種數據結構:圖(graph),它表明了物件與物件之間的“多對多”的一種復雜關系。圖包含了兩個基本元素:頂點(vertex, 簡稱V)和邊(edge,簡稱E)。

有向圖與無向圖

  如果給圖的每條邊規定一個方向,那么得到的圖稱為有向圖。在有向圖中,從一個頂點出發的邊數稱為該點的出度,而指向一個頂點的邊數稱為該點的入度。相反,邊沒有方向的圖稱為無向圖。

有權圖與無權圖

  如果圖中的邊有各自的權重,得到的圖是有權圖。比如地鐵路線圖,連接兩站的邊的權重可以是距離,也可以是價格,或者其他。反之,如果圖的邊沒有權重,或者權重都一樣(即沒有區分),稱為無權圖。

連通圖

  如果圖中任意兩點都是連通的,那么圖被稱作連通圖。圖的連通性是圖的基本性質。無向圖中的一個極大連通子圖稱為其的一個連通分量。有向圖中,如果對任意兩個頂點

與 都存在 到 以及 到 的路徑,則稱其為強連通圖,對應有強連通分量的概念。

圖的存儲

  常用的存儲方式有兩種:鄰接矩陣和鄰接表。

鄰接矩陣

  采用一個大小為

的矩陣 ,對于有權圖, 可以表示 到 的邊的權重,如果是無權圖,則可設為1表示存在邊,0表示不存在邊。因此鄰接矩陣的表示相當的直觀,而且對于查找某一條邊是否存在、權重多少非常快。但其比較浪費空間,對稠密圖( )來說,會比較適合。

  一般情況下我用的鄰接矩陣的結構和初始化代碼如下,具體會根據使用需求有所改動。

struct GNode{int Nv; // number of verticesint Ne; // number of edgesint G[MaxVNum][MaxVNum]; }; typedef struct GNode *MGraph; MGraph Graph = new GNode[MaxVNum];// 初始化圖 void InitGraph(int N,int E){Graph->Nv = N;Graph->Ne = E;for (int u=0;u<=N;u++)for (int v=0;v<=N;v++)Graph->G[u][v]=INF; } //插入邊(這里是有權重的無向邊) void InsertEdge(int u,int v,int weight){Graph->G[u][v]= weight;Graph->G[v][u]=weight; }

鄰接表

為一個大小為 的指針數組, 對應每行存儲一個鏈表,如 中存儲從頂點 出發的邊,如從頂點1出發的邊有 和 其在 中存儲的形式如下圖所示。鄰接表比較適合稀疏圖( )

鄰接表存儲圖。

  另外,之前做習題的時候也會習慣用個二維數組來存儲鄰接表,比如上面的

  一般情況下我用的代碼如下,會根據具體需求有大改動。

typedef struct VNode *AdjNode; struct VNode{int node;AdjNode next; }; AdjNode Graph = new VNode[MaxV];void InitGraph(int N,int E){Graph->Nv = N;Graph->Ne = E;for (int i=1;i<=N;i++){(Graph+i)->node=i;(Graph+i)->next=NULL;} } // 插入有向邊 void InsertEdge(int u,int v){AdjNode tmp1=new VNode;AdjNode tmp2 = new VNode;tmp1->node = v;tmp1->next = (Graph+u)->next;(Graph+u)->next=tmp1;tmp2->node = u;tmp2->next = (Graph+v)->next;(Graph+v)->next=tmp2; }

2. 圖的遍歷

  圖的遍歷最常用的有兩種:深度優先搜索(Depth-first Search, DFS)和廣度優先搜索(Breadth-First Search, BFS)。

深度優先搜索

有點類似于樹的前序遍歷,即從一個選定的點出發,選定與其直接相連且未被訪問過的點走過去,然后再從這個當前點,找與其直接相連且未被訪問過的點訪問,每次訪問的點都標記為“已訪問”,就這么一條道走到黑,直到沒法再走為止。沒法再走怎么辦呢?從當前點退回其“來處”的點,看是否存在與這個點直接相連且未被訪問的點。重復上述步驟,直到沒有未被訪問的點為止。

  從上述文字表述可以看出,DFS很適合使用遞歸,其代碼如下:

int visited[MaxN]={0}; //記錄頂點的訪問狀態 int result[MaxN]; //result數組記錄DFS遍歷結果 int N,E,k;// N為頂點數,E為邊數,k記錄遍歷結果下標void DFS(int v){visited[v]=1;result[k++]=v;for (int i=0;i<N;i++){if (G[v][i]==1 && visited[i]==0)DFS(i);} }int main(){for (int i=0;i<N;i++){k = 0;if (visited[i]==0){DFS(i);//用{}打印出一個連通分量cout<<"{ ";for (int j=0;j<k;j++)cout<<result[j]<<' ';cout<<"}"<<endl;}} return 0; }

廣度優先搜索

有點類似于樹的層序遍歷,也就是像剝洋蔥一樣,“一層一層地剝開???”。即從一個選定的點出發,將與其直接相連的點都收入囊中,然后依次對這些點去收與其直接相連的點。重復到所有點都被訪問然后結束。

  類似樹的層序遍歷,BFS同樣可以通過一個隊列來實現,代碼如下:

int visited[MaxN]={0}; //記錄頂點的訪問狀態 int result[MaxN]; //result數組記錄BFS遍歷結果 int N,E,k;// N為頂點數,E為邊數,k記錄遍歷結果下標void BFS(int v){queue<int> q;q.push(v);visited[v]=1;result[k++]=v;while(!q.empty()){int u = q.front();q.pop();for(int i=0;i<N;i++){if (G[u][i]!=0 && visited[i]!=1){visited[i]=1;q.push(i);result[k++]=i;}}} }int main(){for (int i=0;i<N;i++){k = 0;if (visited2[i]==0){BFS(i);//用{}打印出一個連通分量cout<<"{ ";for (int j=0;j<k;j++)cout<<result[j]<<' ';cout<<"}"<<endl;}} return 0; }上述BFS和DFS的時間復雜度均為 。

3. 最短路徑

  簡而言之,最短路徑就是找圖中連接兩個頂點所有邊權重和最小的路徑。

  • 對無權圖而言,即是找邊最小的路徑;
  • 如果給定起點,則是單源最短路徑,即從一固定起點到任意終點的最短路徑;
  • 如果起點不確定,則是多源最短路徑,即求任意兩個頂點的最短路徑。

單源最短路徑

  對于無向圖而言,可以借助BFS的“剝洋蔥”特性,看從起點到終點需要剝幾個洋蔥圈,剝的層數即是最短路徑長度。

  對于有向圖而言,就不得不提到一個煊赫的名字:Dijkstra算法。陸續泛聽了幾個版本的數據結構,這個名字簡直太深入人心。

  • Dijkstra算法

  具體來說,Dijkstra算法的核心在于:從起點(或者說源點)開始,將其裝進一個“袋子”里,然后不斷往這個袋子里搜羅頂點,當頂點收進去后,能保證從源點到該頂點的當前最短路徑是確定的。每次收錄的頂點是在未收錄的集合里尋找最短路徑最小的點(即離源點最近的點),然后將與收進去的頂點直接相連的點的最短路徑進行更新。

  如下圖所示,

是新鮮被收錄的點, 等是和 有直接相連的邊的點,那么這些點的距離會在 收錄后立馬被更新。

  值得注意的是:Dijkstra算法不適用于存在負權重邊的圖。

  給出Dijkstra代碼前先對保存最短路徑的數組dist[]和收錄狀態的數組collected[]進行初始化:和源點直接相連的點的最短路徑即為該邊的權重,其余均初始化為一個很大的(一看就不可能的數)INF;collected中所有值初始化為false。

int dist[MaxN]; bool collected[MaxN];void InitDist(int start){for (int i=0;i<Graph->Nv;i++){dist[i]=Graph->G[start][i];collected[i]=false;} }

  然后,我們需要尋找下一步被收錄的點:即在所有未被收錄的點中尋找dist最小的點。如果不存在(最小值為無窮大INF),則返回錯誤標識-1。這一步找最小值,我們可以每次都將所有頂點掃描一遍獲得,也可以很自然地想到將dist存在一個最小堆(優先隊列)里。對于前者,因為每次收錄都要掃描一次所有頂點,有

復雜度,然后每條邊都會掃描一次,時間復雜度為 。對于后者,每次收錄時找到目標的時間為 ,有 復雜度,而每次掃描邊然后更新dist的時間也是 ,有 復雜度,所以總的復雜度為 。int findMinDist(int start){int MinD = INF;int MinV;for (int end =0;end<Graph->Nv;end++){if (collected[end]==false && dist[end]<MinD){MinD=dist[end];MinV=end;}}if (MinD<INF)return MinV;elsereturn -1; }

  然后是Dijkstra算法主程序:在初始化后,先將源點收錄進袋,并且源點的最短路徑設為0, 然后開始循環尋找能被收錄的點:如果接受到的是錯誤標識-1,說明找不到符合要求的點了,程序結束。如果找到了,則先將這個點收錄進去,記錄為s; 然后遍歷從s出發的所有邊,對沒有收錄的點,更新他們的最小路徑值。更新的依據是:如果這個點t的當前的最小路徑值,大于s點的最小路徑值與s到t邊的權重值之和,則更新為

,否則維持原狀。bool dijkstra(int start){InitDist(start);dist[start]=0;collected[start]=true;int s;while (1){s = findMinDist(start);if (s==-1)break;collected[s]=true;for (int t=0;t<Graph->Nv;t++){// if W is not collected and s->t existsif (collected[t]==false && Graph->G[s][t]<INF){// check if it is a negative-weighted edgeif (Graph->G[s][t]<0) return false;// check if dist[t] needs updateif (dist[t]>dist[s]+Graph->G[s][t])dist[t]=dist[s]+Graph->G[s][t];}}}return true; }

多源最短路徑

  按照上面已有的Djikstra算法,可以直接將Djikstra算法在每個頂點調用一遍,然后找最小值。時間復雜度為:

直接找dist最小: 。
最小堆維護dist: 。

  另外還有一種更為直接的算法是Floyd算法,且代碼看著簡潔直觀優美。。。

bool Floyd(){int i, j, k;for (k=0;k<Graph->Nv;k++)for (i=0;i<Graph->Nv;i++)for (j=0;j<Graph->Nv;j++)if (dist[i][k]+dist[k][j]<dist[i][j]){dist[i][j]=dist[i][k]+dist[k][j];if (i==j && dist[i][j]<0) //發現負值回路return false;}return true; }

  三重循環嵌套,可以直觀看出時間復雜度為

。意思也很明白,對于每對源點i和終點j,找一個跳板k,看是否經過跳板k距離會比現有的從i到j的距離短,如果是,則更新;否則不作為。發現負值回路的話解不存在,錯誤返回。

參考資料:

  • coursera的《算法分析與設計》(以前的課程)
  • MOOC網的《數據結構與算法》
  • 小結

      距離上次更新已經有很長的時間,一方面是因為沒整理好,包括現在記錄的也覺得很混亂,就這么先把筆記記下來吧,不然時間久了更迷糊。另一方面是身體原因,年紀輕輕竟然椎間盤膨出了,不知道知乎上有沒有啥治療良方,反正去了好幾次醫院都說沒事,直到痛得不行讓拍CT了才知道是腰椎間盤膨出???

       扯遠了???,剩下還有一些最小生成樹,和拓撲排序啥的等我更明白點再寫吧~~~

    總結

    以上是生活随笔為你收集整理的noj数据结构稀疏矩阵的加法十字链表_数据结构之:图的全部內容,希望文章能夠幫你解決所遇到的問題。

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