簡書盡然不支持LaTex公式編輯!!!!
圖
有向圖與無向圖的區別在于邊集合E是否有相,另外表示的區別在于$e = (A, B);e1 = <A, B>$
- 完全無向圖:任意兩點之間存在邊,n個點的邊數$C_n^2$
- 完全有向圖:任意兩點之間存在兩條方向互反的邊
- 子圖:圖G1的頂點集合和邊集合均為圖G2的子集則稱G1位G2的子圖
- 無向圖頂點的度:頂點關聯邊的數量記為D(v)
- 有相圖頂點的出度和如度D(v) = ID(v) + OD(v)
- 回路、環:把所有頂點鏈接的路徑,第一個和最后一個頂點不重復的路徑稱為簡單環。
- 無向圖中連通圖:任意兩個頂點存在路徑連通
- 極大連通子圖
- 有向圖的強連通圖:任意兩個頂點可達
- 連通圖的生成樹:極小連通子圖,包含n個頂點只有n-1條邊;如果有一個有向圖僅有一個頂點入度0,其他頂點入度均為,則為一顆有相樹。
- 圖的邊上加上權重則為網
圖的存儲結構
- 鄰接矩陣
- 鄰接表:邊相對于頂點較少的圖用矩陣表示是一種浪費,鏈式存儲。
- 十字鏈表:
- 多重鏈表
圖的遍歷
- 深度優先
# encoding: utf-8
"""
@version: python3.5.2
@author: kaenlee @contact: lichaolfm@163.com
@software: PyCharm Community Edition
@time: 2017/9/28 9:04
purpose:算法復雜度,最壞情況,每個點都不可達,每個點為基礎遍歷其他點N^2次
"""
import numpy as np
# 有6個頂點的無向圖鄰接矩陣如下:0-5
graphMat = np.array([[0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 1],
[0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1],
[0, 0, 1, 0, 0, 1],
[1, 1, 0, 1, 1, 0]])
# 深度優先遍歷
def travel(graphMat):
n = len(graphMat)
visited = np.zeros(n)
def DFS(graphMat, startPoint):
"""
:param graphMat:
:param startPoint: 開始訪問起點
:return:
"""
nonlocal visited
visited[startPoint] = 1 # 用來記錄該點是否被訪問
n = len(graphMat[startPoint])
for i in range(n):
# 從連通的最右邊開始遍歷那些沒有被訪問到的下一個節點
if (graphMat[startPoint][i] == 1) and (visited[i] == 0):
# 該點和startpoint為連通且沒有被訪問到
print('visited', i)
DFS(graphMat, i)
for j in range(n):
# 從0點開始訪問, 為連通的圖(即任意兩點可達,概念與markov的一樣),循環之后一次就訪問所有的點
print('visited***', j)
DFS(graphMat, j)
if all(visited == 1):
break
travel(graphMat)
#------------------------------------out put-----------------------------------
visited*** 0
visited 1
visited 2
visited 4
visited 5
visited 3
-
廣度優先
如果說深度便是類似于數的前序遍歷,那么廣度就類似于層次遍歷
捕獲.JPG
第一層A進棧
第二BF進, A出
第三層CIG進, B出
第三層E進, F出
....
def travel_BFS(graphMat):
n = len(graphMat)
visited = np.zeros(n)
temp = [] # 創建一個空棧
for i in range(n):
# 如果連通僅僅在這里循環一遍即可
temp.append(i) # 初始進棧
visited[i] = 1
print('visited', i)
while temp:
root = temp.pop(0)
for j in range(n):
if (graphMat[root][j] == 1) and (visited[j] == 0):
temp.append(j)
visited[j] = 1
print('visited', j)
if all(visited == 1):
break
travel_BFS(graphMat)
# ---------------------------out---------------------------------
visited 0
visited 1
visited 3
visited 5
visited 2
visited 4
最小生成樹
對于一個網的最小生成樹:將n個頂點用n-1條邊連接,且邊的權重之和最小。
- 譜里姆算法:一種歸并點的算法,U為需要生成樹的點,在U中隨機選取一個點作為初始點v0并從U中剔除v0,搜選v0的鄰近點中邊權重最小的v1作為最小生成樹的一部分,并從U中剔除v1,。然后考慮v1和v0的所有臨近點,選取邊權重最小點v3并從U中剔除v3,如此下去直到U為空。參考
# encoding: utf-8
"""
@version: python3.5.2
@author: kaenlee @contact: lichaolfm@163.com
@software: PyCharm Community Edition
@time: 2017/9/30 10:44
purpose:
"""
import sys
import numpy as np
def prim(graphMat):
n = len(graphMat)
U = list(range(n))
# 以能生成的最下邊為起點
start = None
value = sys.maxsize
for i in U:
for j in U:
if graphMat[i, j] < value:
value = graphMat[i][j]
start = i
print(start)
V = [start]
U.remove(start)
total = 0 # 總權重
E = [] # 保存連接點邊信息
while U:
# 循環直到U為空
minWay = None
minE = sys.maxsize
minV = None
for v in V:
for u in U:
if graphMat[v][u] < minE:
minE = graphMat[v][u]
minV = u
minWay = [u, v]
E.append(minWay)
V.append(minV)
U.remove(minV)
total += minE
return total, E
graphMat = np.array([[0, 6, 1, 5, sys.maxsize, sys.maxsize],
[6, 0, 5, sys.maxsize, 3, sys.maxsize],
[1, 5, 0, 5, 6, 4],
[5, sys.maxsize, 5, 0, sys.maxsize, 2],
[sys.maxsize, 3, 6, sys.maxsize, 0, 6],
[sys.maxsize, sys.maxsize, 4, 2, 6, 0]])
print(prim(graphMat))
-----out
(15, [[2, 0], [5, 2], [3, 5], [1, 2], [4, 1]])
- 克魯斯卡爾算法:(1)構造一個只含n個頂點,邊集為空的子圖。若將圖中各個頂點看成一棵樹的根節點,則它是一個含有n棵樹的森林。(2)從網的邊集 E 中選取一條權值最小的邊,若該條邊的兩個頂點分屬不同的樹,則將其加入子圖。也就是說,將這兩個頂點分別所在的兩棵樹合成一棵樹;反之,若該條邊的兩個頂點已落在同一棵樹上,則不可取,而應該取下一條權值最小的邊再試之(3)依次類推,直至森林中只有一棵樹,也即子圖中含有 n-1條邊為止。參考
import numpy as np
import sys
from itertools import combinations
def kruskal(graphMat):
# 將矩陣轉化為{邊:權重, ...}并排序
n = len(graphMat)
E = {}
for i in range(n):
for j in range(n):
if (i == j):
pass
else:
E.update({str(i)+str(j): graphMat[i, j]})
E = sorted(E.items(), key=lambda x: x[1])
V = []
U = list(range(n))
saveEs = []
totol = 0
for e, w in E:
p1, p2 = int(e[0]), int(e[1])
if (p1 in V) and (p2 in V):
# 頂點p1 p2都在V中就形成了環路,不可取
pass
else:
V.extend([p1, p2])
saveEs.append([p1, p2])
totol += w
if set(U) == set(V):
break
print('return', saveEs)
# 雖然這樣將所有的樣本點都取到,但可能并不是一個環,沒有形成回路
numOfGroups = n - len(saveEs)
# print(numOfGroups)
if numOfGroups == 1:
return totol, saveEs
else:
# 整理出分組并在每個分組,取一條邊作為連接回路
def findShare(X, tag):
newtag = tag.copy()
for i in X:
if set(i) & set(tag):
newtag.extend(list(set(i) - set(tag)))
if newtag == tag:
return newtag
else:
return findShare(X, newtag)
groups = []
for i in saveEs:
if groups:
flag = 0
# 如果改點已經存在groups中不在計算
for j in groups:
if set(j) & set(i):
flag = 1
break
if flag:
continue
one = findShare(saveEs, i)
groups.append(one)
print(groups)
asset = list(combinations(groups, 2))
print('asset', asset)
# 找出鏈接每兩個group之間的最短連接
briges = []
for g1, g2 in asset:
minBrige = sys.maxsize
brige = None
for i in g1:
for j in g2:
if graphMat[i][j] < minBrige:
minBrige = graphMat[i][j]
brige = [i, j]
briges.append(brige)
# n個group之間僅僅需要n-1個連接
maxbrige = None
value = 0
for i, j in briges:
totol += graphMat[i, j]
if graphMat[i, j] > value:
maxbrige = [i, j]
briges.remove(maxbrige)
saveEs.extend(briges)
i, j = maxbrige
return totol - graphMat[i, j], saveEs
graphMat = np.array([[0, 6, 1, 5, sys.maxsize, sys.maxsize],
[6, 0, 5, sys.maxsize, 3, sys.maxsize],
[1, 5, 0, 5, 6, 4],
[5, sys.maxsize, 5, 0, sys.maxsize, 2],
[sys.maxsize, 3, 6, sys.maxsize, 0, 6],
[sys.maxsize, sys.maxsize, 4, 2, 6, 0]])
print(kruskal(graphMat))
---out
return [[0, 2], [3, 5], [1, 4]]
[[0, 2], [3, 5], [1, 4]]
asset [([0, 2], [3, 5]), ([0, 2], [1, 4]), ([3, 5], [1, 4])]
(15, [[0, 2], [3, 5], [1, 4], [2, 5], [2, 1]])