大話數據結構-圖(持續更新)

簡書盡然不支持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,其他頂點入度均為,則為一顆有相樹。
  • 圖的邊上加上權重則為

圖的存儲結構

  • 鄰接矩陣
  • 鄰接表:邊相對于頂點較少的圖用矩陣表示是一種浪費,鏈式存儲。
  • 十字鏈表:
  • 多重鏈表

圖的遍歷

  1. 深度優先
# 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
  1. 廣度優先
    如果說深度便是類似于數的前序遍歷,那么廣度就類似于層次遍歷
捕獲.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]])
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容

  • 第一章 緒論 什么是數據結構? 數據結構的定義:數據結構是相互之間存在一種或多種特定關系的數據元素的集合。 第二章...
    SeanCheney閱讀 5,795評論 0 19
  • https://zh.visualgo.net/graphds 淺談圖形結構https://zh.visualgo...
    狼之獨步閱讀 4,179評論 0 0
  • 1 數據2 算法3 線性表4 棧5 隊列6 串樸素模式匹配算法 -子串的定位操作:從主串中找到子串KMP模式匹配算...
    oldSix_Zhu閱讀 1,511評論 0 4
  • 圖是一種比線性表和樹更復雜的數據結構,在圖中,結點之間的關系是任意的,任意兩個數據元素之間都可能相關。圖是一種多對...
    Alent閱讀 2,323評論 1 22
  • 我不怕單身一輩子,就是怕在我不懂愛的時候遇到我真正愛的人。 L和我說這句話的時候,眼神中充滿著留戀,卻同樣充滿著失...
    玖溪成霖閱讀 508評論 4 1