圖與網絡的基本概念與數據結構
一.圖與網絡的基本概念
圖論中圖是由點和邊構成的,可以反映一些對象之間的關係。
無向圖
無向圖(簡稱圖):沒有方向,由點和邊構成的圖,記做G =(V , E),點是V,邊是E。
注:圖論的圖與幾何圖、工程圖不一樣。
因爲一般情況下的圖中點的相對位置及點間連線長短,對於反映對象之間的關係並不是重要的。
聯結 和 的鏈:在無向圖G中,若存在一個點邊的交錯序列()其中屬於V(G),屬於E(G)
聯結 v_{i1} 和 v_{ik} 的圈:在上述的鏈中,若 v_{i1} = v_{ik},也就是首尾相連。
連通圖:對於一個無向圖,若任何兩個不同的點之間,至少存在一條鏈。
簡單圖:一個圖如果它既沒有環也沒有平行邊(兩條邊連接同一對頂點),稱爲簡單圖(simple graph)。
完全圖:其中每對不同的頂點之間都恰連有一條邊相連
賦權圖:對無向圖的每一條邊() ,都有一個數(稱爲權重) 對應。
二分圖又稱作二部圖,是圖論中的一種特殊模型。 設G=(V,E)是一個無向圖,如果頂點V可分割爲兩個互不相交的子集(A,B),並且圖中的每條邊(i,j)所關聯的兩個頂點i和j分別屬於這兩個不同的頂點集(i in A,j in B),則稱圖G爲一個二分圖
頂點的度:與頂點v關聯的邊的個數稱爲頂點v 的
度(degree) (環計兩度),記作 d(v) .
度爲零的點稱爲弧立點,度爲1的點稱爲懸掛點。懸掛點的關聯邊稱爲懸掛邊。度爲奇數的點稱爲奇點(odd point) ,度爲偶數的點稱爲偶點(even point) 。
有向圖
有向圖:由點和弧構成的圖,記做D =(V , A),其中V是圖D的點集,A是圖D的弧集。
注:無向圖是一種特殊的有向圖,無向圖的邊實際上就是等 價於兩條方向相反的弧。
聯結 和 的路:在無向圖G中,若存在一個點邊的交錯序列()其中屬於V(D),屬於V(D)
*聯結 v_{i1} 和 v_{ik} 的迴路:在上述的鏈中,若 v_{i1} = v_{ik},也就是首尾相連。
賦權圖:對無向圖的每一條弧() ,都有一個數(稱爲權重) 對應。
網絡:在賦權的有向圖D中指定一點爲發點(記爲 ),指定另一點爲收點(記爲),其餘的點爲中間點,並把 D 中的每一條弧的賦權數 稱爲弧 的容量 ,這樣的賦權有向圖 D 就稱爲網絡。
二.圖與網絡的數據結構
描述圖與網絡的3種常用表示方法:鄰接矩陣表示法、關聯矩陣表示法、弧表表示法
鄰接矩陣表示法
鄰接矩陣表示法是將圖以鄰接矩陣(adjacency matrix)的形式存儲在計算機中。圖 G=(V,A) 的鄰接矩陣是如下定義的:C是一個的0-1矩陣,即
也就是說,如果兩節點之間有一條弧,則鄰接矩陣中對應的元素爲1;否則爲0。
例一:對於下圖,可以用鄰接矩陣表示爲
解釋:現在有五個圖,鄰接矩陣a就是的方陣,∵ 1→2,∴a[1][2]=1;
∵1→3所以a[1][3]=1;∵ 4→3,∴a[4][3]=1,其他都是一樣的,所有的都寫出來就是:
同樣,對於網絡中的權,也可以用類似鄰接矩陣的矩陣表示。只是此時一條弧所對應的元素不再是1,而是相應的權而已。如果網絡中每條 弧賦有多種權,則可以用多個矩陣表示這些權。
關聯矩陣表示法
關聯矩陣表示法是將圖以關聯矩陣(incidence matrix)的形式存儲在計算機中.
圖 的關聯矩陣B是如下定義的:B是一個n*m的矩陣,即
如果一個頂點是一條弧的起點,則關聯矩陣中對應的元素爲1;如果一個頂點是一條弧的終點,則關聯矩陣中對應的元素爲-1;如果一個頂點與一條弧不關聯,則關聯矩陣中對應的元素爲0。
例2 對於例1所示的圖,如果關聯矩陣中每列對應弧的順序爲 (1,2),(1,3),(2,4),(3,2),(4,3),(4,5),(5,3)和(5,4),則關聯矩陣表示爲(列單位爲弧)
這個圖有4個節點,7條連線,所以是的矩陣,每一列都是隻有一個1,一個 -1。
看e1這條線,頭是v1尾是v2,所以v1=1,v2=-1
e3,頭是v3,尾是v4,所以v3是1,v4是-1;
)
弧表表示法
弧表表示法將圖以弧表(arc list)的形式存儲在計算機中。所謂圖的弧表,也就是圖的弧集合中的所有有序對。
假設例1中的弧(1,2),(1,3),(2,4),(3,2),(4,3),(4,5),(5,3)和(5,4)上的權分別爲8,9,6,4,0,3,6和7,則弧表表示如下:
最短路問題 (Shortest Path Problem)
最短路問題:對一個賦權有向圖中指定的兩個點和找到條從到的路,使得這條路上所有弧的權數總和最小,這條路被稱爲從 到, 的最短路,這條路_上所有弧的權數的總和被稱爲從到 的距離。
Dijkstra算法
如上圖,帶權值的最短路徑長度就是黃線標出來的 爲7
求解最短路的迪克斯特拉Dijkstra算法(雙標號法)
雙標號即:對圖中的點 賦予兩個標號 ,其中 l_j 表示從起點 到 的最短路的長度, 表示在 至 的最短路上前面一個鄰點的下標,從而找到所求的最短路及其距離。
- 先給起點 標號:(0,s),表示從 到 的距離爲0, 爲起點。
- 找出已標號的點集 I ,未標號的點集 J 以及從 I 到 J 的弧集。
- 若上述弧集是空集,則計算結束。此時,如果 已標號 (l_t,k_t) 則 到 的距離爲 ,而從 到 的最短路,可以從 反向追蹤到起點 而得到。 如果 未標號,則不存在從 到 的有向路。 若上述弧集不是空集,則轉下一步。
- 對上述弧集中的每一條弧,計算 在所有的中,找值最小的弧,不妨設爲則給此弧的終點 標號 ,返回步驟2.
標號(m,n)m是權重,n是前一個節點
同時,我們可以從各點的標號得到起點到該點的最短路徑及距離。
用行向量pb表示P標號信息,index1表示標號頂點順序, index2表示標號頂點索引,d表示最短路的值.
clc,clear
a=zeros(6); %鄰接矩陣初始化
a(1,2)=50;a(1,4)=40;a(1,5)=25;a(1,6)=10;
a(2,3)=15;a(2,4)=20;a(2,6)=25;
a(3,4)=10;a(3,5)=20;
a(4,5)=10;a(4,6)=25;
a(5,6)=55;
a=a+a';
a(a==0)=inf;
pb(1:length(a))=0;pb(1)=1;index1=1;index2=ones(1,length(a));
d(1:length(a))=inf;d(1)=0;
temp=1; %最新的P標號的頂點
while sum(pb)<length(a)
tb=find(pb==0);
d(tb)=min(d(tb),d(temp)+a(temp,tb));
tmpb=find(d(tb)==min(d(tb)));
temp=tb(tmpb(1)); %可能有多個點同時達到最小值,只取其中的一個
pb(temp)=1;
index1=[index1,temp];
temp2=find(d(index1)==d(temp)-a(temp,index1));
index2(temp)=index1(temp2(1));
end
d, index1, index2
程序過程就不具體分析了,只看看輸出吧。
d代表距離,在這個題裏代表票價,代表c1分別到c1,c2,c3,c4,c5,c6的距離
index1代表標號順序
index2代表該標號的前一個標號
d =
0 35 45 35 25 10
index1 =
1 6 5 2 4 3
index2 =
1 6 5 6 1 1
第一個標號的是自身c1,不用管,從第二個開始c1→c6,可以看到c6的前一個節點是1,所以c1直接到了c6.距離d=10;
第三個是c5,也是c1直接到的,距離d=25;
第四個c2,可以看到c2的前一個節點是c6,c6的前一個節點是c1,所以c1到c2的最短距離爲c1先到c6再從c6到c2,距離爲40
其他的以此類推
Floyd算法
主要用來計算每對頂點之間的最短路徑。
Dijkstra算法用來計算一對頂點之間的最短路徑。
Floyd算法的基本思想是:遞推產生一個矩陣序列 ,其中 表示從頂點到頂點 的路徑上所經過的頂點序號不大於k的最
短路徑度。迭代公式爲:
k是迭代次數,最後,當k=n時, 即是各頂點之間的最短通路值。
這個算法要產生兩個矩陣
這個算法就是找一箇中間點,通過判斷兩個頂點的直接距離與通過中間點的簡介距離的遠近比較(近的爲優) 來不斷對兩個矩陣進行更新。
A代表每兩個點之間的距離,path是每兩個點之間的最短路徑經過的中間點,一開始沒有中間點,所以設置爲-1,代表沒有中間點。
這個是找一箇中間點,一般是從0開始,再找1 2 3,這樣過一遍。
我們直接用1開始吧,因爲我算了,0沒有更新。
算自身和自身都是0沒啥意思,兩兩連接就是這12對頂點。
{0, 1}, {0, 2}, {0, 3}, {1, 0}, {1, 2}, {1, 3),
{2, 0}, {2, 1}, {2, 3}, {3, 0}, {3, 1}, {3, 2}
- {0,1}:因爲要經過1,所以可以看成是,左右相等,不更新
- {0,2}:所以更新,Path矩陣的A[0][2]更新爲1(因爲中間點是1)A矩陣中的A[0][2]更新爲9
- 這是經過中間點爲1跟新後的兩個新矩陣
- {0,3}:不更新
- {1,2}{1,3}{1,0}都經過1,不更新,剩下的自己算吧,當然我算了,沒有更新。
再以頂點2爲中間點進行檢測
- {0,1}:不更新
- {0,2}{2,0} {2,1}{2,3}有2不用算
- {0,3}:不更新
- {1,0}: 更新
- {3,0}不更新
- 兩個矩陣都分別跟新爲如下兩個
- 後面也都是一樣,上次更新後直接以上次更新的那個矩陣爲基礎進行計算。這是以2爲中間點進行更新的結果
- 這是3爲中間點進行更新的結果
弗洛伊德算法就是利用矩陣的不斷更新進行運算
當然FLoyd算法也有MATLAB程序
以Dijkstra算法那個例題爲例,進行計算
clear;clc;
n=6; a=zeros(n);
a(1,2)=50;a(1,4)=40;a(1,5)=25;a(1,6)=10;
a(2,3)=15;a(2,4)=20;a(2,6)=25; a(3,4)=10;a(3,5)=20;
a(4,5)=10;a(4,6)=25; a(5,6)=55;
a=a+a';
a(a==0)=inf; %把所有零元素替換成無窮
a([1:n+1:n^2])=0; %對角線元素替換成零,Matlab中數據是逐列存儲的
path=zeros(n);
for k=1:n
for i=1:n
for j=1:n
if a(i,j)>a(i,k)+a(k,j)
a(i,j)=a(i,k)+a(k,j);
path(i,j)=k;
end
end
end
end
a, path
a =
0 35 45 35 25 10
35 0 15 20 30 25
45 15 0 10 20 35
35 20 10 0 10 25
25 30 20 10 0 35
10 25 35 25 35 0
path =
0 6 5 5 0 0
6 0 0 0 4 0
5 0 0 0 0 4
5 0 0 0 0 0
0 4 0 0 0 1
0 0 4 0 1 0
可以看到輸出a是一個對稱矩陣,代表兩個點之間的距離,path代表路徑,如[1,2]的數值是6,也就是經過了6這個中間點。
是不是隻有距離這種題才能用這個模型呢?
例
設備更新問題。某公司使用一臺設備,在每年年初,公司就要決定是購買新的設備還是繼續使用舊設備。如果購置新設備,就要支付一定的購置費,當然新設備的維修費用就低。如果繼續使用舊設備,這樣可以省去了購置費,但維修費用就高了。現在需要我們制定一個五年之內的更新設備的計劃,使得五年內購置費和維修費總的支付費用最小。
這種設備每年年初的價格如下表
另外,還已知使用不同時間(年)的設備所需要的維修費如下圖
用點vi 表示第 i 年年初購進一臺新設備,i=1, 2, 3, 4, 5, 6.
用弧 (vi,vj) 表示在第 i 年年初購進的設備一直使用到第 j 年年初,其中i<j
化爲最短路問題後,相應的圖示如下:
相應地,把費用轉化爲圖中弧的權數。
對於弧 (vi,vj) ,其權數爲從第 i 年年初購進設備使用到第 j-1 年年底所花費的購置費及維修費的總和。如下:
下面把計算得到的權數賦到圖上,得到賦權圖,則求解五年
內購置費和維修費總費用最小的問題就轉化爲從賦權圖中求解一
條從 v1 到v6 的最短路問題。
最短路徑:
即:第1年初購置的新設備使用到第3年初 (第2年底) ,第3年初再購置新設備使用到第6年初 (第5年底) 。
即:第1年初購置的新設備使用到第4年初 (第3年底) ,第4年初再購置新設備使用到第6年初 (第5年底)
兩個路徑耗費的金錢是一樣的
用MATLAB
clear;clc;
n=6; a=zeros(n);
a(1,2)=16;a(1,3)=22;a(1,4)=30;a(1,5)=41;a(1,6)=59;
a(2,3)=16;a(2,4)=22;a(2,5)=22;a(2,6)=41;
a(3,4)=17;a(3,5)=23;a(3,6)=31;
a(4,5)=17;a(4,6)=23;
a(5,6)=18;
a=a+a';
a(a==0)=inf; %把所有零元素替換成無窮
a([1:n+1:n^2])=0; %對角線元素替換成零,Matlab中數據是逐列存儲的
path=zeros(n);
for k=1:n
for i=1:n
for j=1:n
if a(i,j)>a(i,k)+a(k,j)
a(i,j)=a(i,k)+a(k,j);
path(i,j)=k;
end
end
end
end
a, path
下一篇數學建模之圖論——圖與網絡模型(二)(最小生成樹問題、最大流問題) https://blog.csdn.net/weixin_45755332/article/details/106946149