一. 复习图(Graph)的表示法
G r a p h G = ( V , E ) Graph G=(V,E) GraphG=(V,E)
- V V V是所有的顶点的集合
-
E
E
E是所有的连接顶点的边的集合,
E
E
E
无向图, E E E包含的边是没有方向性的。
有向图, E E E包含的边是有方向性的。
图的一些属性
- ∣ E ∣ = O ( V 2 ) |E|=O(V^2) ∣E∣=O(V2)
- 如果G是连通的,即从任意两个顶点之间有通路存在, ∣ E ∣ > ∣ V ∣ − 1 |E|>|V|-1 ∣E∣>∣V∣−1
- l g ( ∣ E ∣ ) = θ ( l g ( ∣ V ∣ ) ) lg(|E|) = \theta(lg(|V|)) lg(∣E∣)=θ(lg(∣V∣))
图的表示方法
- 连接矩阵
G
=
(
V
,
E
)
G=(V,E)
G=(V,E), 当
V
=
1
,
2
,
.
.
.
,
n
V={1,2,...,n}
V=1,2,...,n时nxn矩阵A可以标记为
A [ i , j ] = { 1 当 ( i , j ) ∈ E 0 当 ( i , j ) ∉ E A[i,j]=\begin{cases} 1 &\text{当} (i,j)\in E \\ 0 &\text{当} (i,j)\notin E \end{cases} A[i,j]={10当(i,j)∈E当(i,j)∈/E
适合用这种方式表示的图:完全图
- 连接列表,对于顶点集合
V
∈
A
V\in A
V∈A,其连接列表可以表示为
A d j [ 1 ] = 2 , 3 Adj[1]={2,3} Adj[1]=2,3
A d j [ 2 ] = 3 Adj[2]={3} Adj[2]=3
A d j [ 3 ] = Adj[3]={} Adj[3]=
A d j [ 4 ] = 3 Adj[4]={3} Adj[4]=3
∣ A d j [ v ] ∣ = { 顶点的度数 无向图 顶点的出度 有向图 |Adj[v]|=\begin{cases} 顶点的度数 &\text{无向图} \\ 顶点的出度 &\text{有向图} \end{cases} ∣Adj[v]∣={顶点的度数顶点的出度无向图有向图
无向图中的边可以称作握手(handshaking), ∑ v ∈ V d e g r e e ( V ) = 2 ∣ E ∣ \sum_{v\in V}degree(V)=2|E| ∑v∈Vdegree(V)=2∣E∣。
二. 最小生成树(Minimum Spanning Tree - MST)
输入: 连通的无向图
G
=
(
V
,
E
)
G=(V, E)
G=(V,E)由有权重的边相连接,
N
:
E
→
R
N:E\to R
N:E→R
*为了简化问题,我们假设每条边的权重都不一样
输出一个包含所有顶点的最小总权重的树
W
(
T
)
=
Σ
(
u
,
v
)
∈
T
W
(
u
,
v
)
W(T)=\Sigma_{(u,v)\in T} W(u,v)
W(T)=Σ(u,v)∈TW(u,v)
如下图:
最优子结构
一棵最优子树如下图所示:
将上图中的
(
u
,
v
)
(u,v)
(u,v)边移出,则T被分成两棵最小生成树子树,
T
1
和
T
2
T_1和T_2
T1和T2。则,
T
1
和
T
2
T_1和T_2
T1和T2是
G
G
G的子图
G
1
和
G
2
G1和G2
G1和G2的最小生成树,其中:
G
1
=
(
V
1
,
E
)
,
V
1
是所有
T
1
的顶点
G1=(V_1,E),V_1是所有T_1的顶点
G1=(V1,E),V1是所有T1的顶点,
G
2
=
(
V
2
,
E
)
,
V
1
是所有
T
2
的顶点
G2=(V_2,E),V_1是所有T_2的顶点
G2=(V2,E),V1是所有T2的顶点。
证明:
如果对图
G
1
G1
G1存在
T
1
′
T_1'
T1′是比
T
1
T_1
T1更优的最小生成树,那么对于图
G
G
G而言
T
′
=
(
u
,
v
)
⋃
T
1
′
⋃
T
2
T'={(u,v)}\bigcup T_1'\bigcup T_2
T′=(u,v)⋃T1′⋃T2必然要比
T
T
T更优的最小生成树,与题设矛盾,故
T
1
T_1
T1必是
G
1
G_1
G1的最小生成树。
证毕
由此,我们看到最小生成树问题是可以分割成最优子结构的,我们第一想到的是动态规划。但是,实际上对于这个问题还存在一个更好的算法 - 贪婪算法(Greed Algorithm)。
三. 贪婪算法的标志
贪婪选择属性: 一个局部的最优选择也是全局的最优选择。
如果 T T T是 G = ( V , E ) G=(V,E) G=(V,E)的最小生成树,并且 A ⊂ V A\subset V A⊂V,假设 ( u , v ) ∈ E (u,v)\in E (u,v)∈E是最小权重的边连接 A 和 V − A A和V-A A和V−A,那么 ( u , v ) ∈ T (u,v)\in T (u,v)∈T。
证明:假设
(
u
,
v
)
∉
T
(u,v)\notin T
(u,v)∈/T,那么必然存在一条路径连接
u
和
v
u和v
u和v,将
(
u
,
v
)
(u,v)
(u,v)和该路径上的第一条边交换,即删除该路径上的第一条边(下图中红色叉标记处),并添加
(
u
,
v
)
(u,v)
(u,v),此时出现了一棵比T更低权重和的最小生成树,与题设矛盾。
证毕
四. Prim‘s 算法 - 贪婪算法
基本思想:维护是一个优先队列 Q Q Q作为 V − A V-A V−A的图中的所有顶点,依次输入每个顶点存在于 Q Q Q列表中且其连接边的权重值最小,并将该顶点一如最小生成树子树 A A A中。
伪代码如下:
最后,{(V, pi([V]}就是最后形成的最小生成树。
算法运行的过程如下图所示
- 第一个顶点是随机选择的
- 有这个点出发更新和他相连的顶点的权重,并将权重值最小的顶点加入生成树
算法效率分析
代码的第一部分的算法复杂度是固定的n。
代码的第二部分的算法复杂度取决于图所包含的顶点数|V|。
代码第二部分中还包含一个循环,其算法复杂度则取决于每个顶点的度,即每个顶点的所连接的边。
所以,总的算法复杂度是
T i m e = θ ( V . T E x t r a c t − m i n + E . T D e c r e a s e − k e y s ) Time = \theta(V.T_{Extract-min}+E.T_{Decrease-keys}) Time=θ(V.TExtract−min+E.TDecrease−keys)
Q Q Q | T E x t r a c t − m i n T_{Extract-min} TExtract−min | T D e c r e a s e − k e y s T_{Decrease-keys} TDecrease−keys | Total |
---|---|---|---|
array | O ( V ) O(V) O(V) | O ( 1 ) O(1) O(1) | O ( V 2 ) O(V^2) O(V2) |
binary | O ( l g V ) O(lgV) O(lgV) | O ( l g V ) O(lgV) O(lgV) | O ( E . l v V ) O(E.lvV) O(E.lvV) |
fib beap | O ( l g V ) a m o r t i z e d O(lgV)_{amortized} O(lgV)amortized | O ( 1 ) a m o r t i z e d O(1)_{amortized} O(1)amortized | O ( E + v . l g V ) O(E+v.lgV) O(E+v.lgV) |