noj數據結構稀疏矩陣的加法十字鏈表_數據結構之:圖

6127d57f9a149d3ea6b44deac18bb457.png

導讀

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出發的邊有
其在
中存儲的形式如下圖所示。鄰接表比較適合稀疏圖(

002005e9e9176c102f43acf402f38017.png
鄰接表存儲圖。

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

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

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算法不適用于存在負權重邊的圖。

45019c9eb1e062ad12ed06edea2a1e50.png

  給出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的距離短,如果是,則更新;否則不作為。發現負值回路的話解不存在,錯誤返回。

參考資料:

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

小結

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

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/538163.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/538163.shtml
英文地址,請注明出處:http://en.pswp.cn/news/538163.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue中用數組語法綁定class

簡單的綁定class就不說了&#xff0c;它可以和對象語法一樣&#xff0c;使用data、computed、methods三種方法。說一下我在工作里體會到這種作法的好處。那么直接上代碼。。。咔咔咔 說下需求&#xff0c;我是做一個顯示框&#xff0c;當status為1時&#xff0c;代表成功狀態&a…

解釋型語言與編譯型語言的區別

編譯型語言在程序執行之前&#xff0c;有一個單獨的編譯過程&#xff0c;將程序翻譯成機器語言&#xff0c;以后執行這個程序的時候&#xff0c;就不用再進行翻譯了。 解釋型語言&#xff0c;是在運行的時候將程序翻譯成機器語言&#xff0c;所以運行速度相對于編譯型語言要慢。…

三星臺式機計算機編號怎么看,三星筆記本如何查看型號

現如今&#xff0c;電腦的用途廣泛&#xff0c;而且方便快捷&#xff0c;深受人們的歡迎&#xff0c;人們不僅可以通過電腦來了解知識&#xff0c;開闊眼界&#xff0c;而且電腦是一種消遣、娛樂的方式&#xff0c;可以放松身心。那電腦的話&#xff0c;有分兩種&#xff0c;一…

自旋鎖和互斥鎖實例_多線程編程之自旋鎖

一、什么是自旋鎖一直以為自旋鎖也是用于多線程互斥的一種鎖&#xff0c;原來不是&#xff01;自旋鎖是專為防止多處理器并發(實現保護共享資源)而引入的一種鎖機制。自旋鎖與互斥鎖比較類似&#xff0c;它們都是為了解決對某項資源的互斥使用。無論是互斥鎖&#xff0c;還是自…

如何卸載symantec

前段時間,業務的虛機上安裝了symantec Endpoint Protection(正版)&#xff0c; 發現虛機運行一段時間就會失去響應死機&#xff0c;并且有些安裝symantec的虛機3389端口無法使用&#xff0c;怎么折騰都不行。最后決定卸載它。一、是否可以用停止服務和終止進程再卸載的方式卸載…

CSS文件引入順序

<link rel"stylesheet" href"bootstrap.min.css"> <link rel"stylesheet" href"app.css"> 自定義的css要最后引入。因為有時候會修改bootstrap的css。只有后引入的才會覆蓋。 如果提前引入了&#xff0c;自定義的會被bo…

瀏覽器的簡單兼容

2019獨角獸企業重金招聘Python工程師標準>>> function getXHER() { var xhr null; if(XMLHttpRequest){ xhr new XMLHttpRequest(); }else{ xhr new ActiveXObject(Microsoft.XMLHTTP); } return xhr; }轉載于:https://my.oschina.net/u/2511906/blog/1865622

用計算機算出陳赫手機號碼,陳赫手機號碼遭《快本》曝光,并被網友打到關機!還有人搜到了他的支付寶賬戶......

原標題&#xff1a;陳赫手機號碼遭《快本》曝光&#xff0c;并被網友打到關機&#xff01;還有人搜到了他的支付寶賬戶...昨天的陳赫可能是被不斷的電話鈴聲叫醒的&#xff0c;因為快本在節目中把陳赫的電話號碼給曝光了……當時導演讓每個明星向自己的一位圈內好友發出求助短信…

伯努利分布方差_統計分布--深入淺出統計學總結

伯努利分布&#xff1a;一個實驗只有兩個結果概率發生在{0,1}&#xff0c;發生一個事件成功的概率為 x&#xff0c;不成功的概率為y&#xff0c; 1.若符合伯努基分布條件&#xff1a;p 成功概率 &#xff0c; q 失敗概率伯努利分布數學期待&#xff1a;伯努利分布方差&#x…

解決The project description file (.project) for 'xxx' is missing

若沒有修改過.project文件&#xff0c;只需重新導入工程即可&#xff0c;別糾結其他的了

macbook禁用鍵盤_一行命令禁用 MacBook 內置鍵盤

去年底阿麥換了新的 MacBook Pro&#xff0c;于是她自學生時代就一直在用的老款 MacBook Pro 就歸我當玩具了。一度考慮過將其出售&#xff0c;但是想到自己還閑置了一塊 SSD&#xff0c;就想著干脆換上讓它繼續服役。于是買了光驅硬盤支架&#xff0c;想著有時間就給換上。然而…

Java分享筆記:自定義枚舉類 使用enum關鍵字定義枚舉類

在JDK1.5之前沒有enum關鍵字&#xff0c;如果想使用枚舉類&#xff0c;程序員需要根據Java語言的規則自行設計。從JDK1.5開始&#xff0c;Java語言添加了enum關鍵字&#xff0c;可以通過該關鍵字方便地定義枚舉類。這種枚舉類有自己的程序編寫規則&#xff0c;并且具有一些特殊…

html5做咖啡網頁素材,HTML5/CSS3咖啡品類切換動畫

CSS語言&#xff1a;CSSSCSS確定body {background-color: #FB9F89;}.container {position: absolute;top: 30px;left: 200px;}.saucer {position: absolute;top: 50px;left: 40px;width: 200px;height: 200px;border-radius: 100%;background-color: #FFF;box-shadow: 5px 1px …

汽車和山羊問題matlab仿真_Matlab----無人機集群對抗中的關鍵問題和仿真平臺(開發中)案例...

無人機集群對抗&#xff0c;是自動駕駛中路徑規劃的新問題&#xff0c;并且連續兩年出現在最近的中國大學生數學建模競賽中。可見&#xff0c;這是一個急需解決的數學問題&#xff08;體現了官方的軍事戰略意志&#xff09;&#xff0c;同時&#xff0c;還沒有成熟解決方案的問…

使用durid的ConfigFilter對數據庫密碼加密

原文連接&#xff1a;http://blog.csdn.net/aixiaoyang168/article/details/49930513 ----------------------------------------------------------------------- 對于大部分程序員來說&#xff0c;數據庫的信息&#xff0c;如用戶名&#xff0c;密碼等信息一般都寫到配置文件…

序(不知道是什么時候的模擬題)

序 【問題背景】 zhx 給他的妹子們排序。 【問題描述】 \(zhx\) 有 \(N\) 個妹子&#xff0c; 他對第 \(i\) 個妹子的好感度為\(a_i\), 且所有\(a_i\),兩兩不相等。 現在 \(N\) 個妹子隨意站成一排&#xff0c; 他要將她們根據好感度從小到大排序。 他使用的是冒泡排序算法&…

html寫用戶導入,用戶基本信息錄入.html

&#xfeff;用戶基本信息錄入$axure.utils.getTransparentGifPath function() { return resources/images/transparent.gif; };$axure.utils.getOtherPath function() { return resources/Other.html; };$axure.utils.getReloadPath function() { return resources/reload.…

adg oracle 架構_技術棧數據中心有了ADG架構就高枕無憂了?你還需要做這一步!...

技術棧數據中心有了ADG架構&#xff0c;就高枕無憂了&#xff1f;你還需要做這一步&#xff01;如果把數據中心建設比喻成西天取經&#xff0c;那旅途上的九九八十一難就是我們不得不躲閃、跨越、攻堅的堡壘。即日起&#xff0c;希嘉推出“技術棧”板塊&#xff0c;集結數據治理…

String length must be a multiple of four.

今天在整理2013年的工作時的一個項目&#xff0c;修改了數據庫連接&#xff0c;初始化數據庫&#xff0c;部署運行報錯&#xff0c;主要原因是阿里巴巴druid報錯&#xff0c;導致DataSource初始化失敗。 druid報錯日志&#xff1a; Caused by: java.lang.IllegalArgumentExce…

論文筆記:Person Re-identification with Deep Similarity-Guided Graph Neural Network

Person Re-identification with Deep Similarity-Guided Graph Neural Network 2018-07-27 17:41:45 Paper&#xff1a; https://128.84.21.199/pdf/1807.09975.pdf 本文將 Graph Neural Network (GNN) 應用到 person re-ID 的任務中&#xff0c;用于 model 不同 prob-gallery …