圖的生成樹算法

代碼來自《數(shù)據(jù)結(jié)構(gòu)與算法Python語言實現(xiàn)》裘宗燕著

# 圖的生成樹

#----------圖的鄰接矩陣實現(xiàn)--------------------
class Graph:

    def __init__(self,mat,unconn = 0):
        # 頂點個數(shù)
        vnum = len(mat)
        # 檢查是否為方陣
        for x in mat:
            if len(x) != vnum:
                raise ValueError("Argument for 'Graph'.")
        
        # 拷貝
        self._mat = [mat[i][:] for i in range(vnum)]
        self._unconn = unconn
        self._vnum = vnum

    # 返回頂點個數(shù)
    def vertex_num(self):
        return self._vnum

    # 驗證序號v是否有效
    def _invalid(self,v):
        return 0>v or v>=self._vnum    
                
    # 添加頂點
    def add_vertex(self):
        raise ValueError("Adj-Matrix does not support 'add_vertex'.")

    # 添加邊雕欺,即添加兩個點之間的關(guān)聯(lián)線段
    def add_edge(self,vi,vj,val=1):
        # 先檢查vi和vj是否有效
        if self._invalid(vi) or self._invalid(vj):
            raise ValueError(str(vi) + 'or' + str(vj) + 'is not a valid vertex.')
        # 為線段附加權(quán)重
        self._mat[vi][vj] = val

    # 獲取邊的權(quán)重
    def get_edge(self,vi,vj):
        if self._invalid(vi) or self._invalid(vj):
            raise ValueError(str(vi) + 'or' + str(vj) + 'is not valid vertex.')
        return self._mat[vi][vj]

    # 獲取指定結(jié)點的所有相連邊
    def out_edges(self,vi):
        if self._invalid(vi):
            raise ValueError(str(vi) + 'is not a valid vertex.')
        return self._out_edges(self._mat[vi],self._unconn)
    
    @staticmethod # 靜態(tài)方法恋沃,無需實例化即可調(diào)用
    def _out_edges(row,unconn):
        edges = []
        for i in range(len(row)):
            if row[i] != unconn:
                edges.append((i,row[i]))
        return edges
    
    # 將矩陣轉(zhuǎn)化為字符串模式
    def __str__(self):
        return "[\n" + ".\n".join(map(str,self._mat)) + "\n]" + "\nUnconnected:" + str(self._unconn)
#----------圖的鄰接矩陣實現(xiàn)--------------------



#----------圖的壓縮鄰接矩陣實現(xiàn)-----------------
class GraphAL(Graph):
    # GraphAL繼承自Graph    
    
    def __init__(self,mat = [],unconn = 0):
        vnum = len(mat)
        # 檢查是否為方陣
        for x in mat:
            if len(x) != vnum:
                raise ValueError("Argument for 'Graph'.")

        # 只復制帶有權(quán)重的邊
        self._mat = [Graph._out_edges(mat[i],unconn) for i in range(vnum)]
        self._vnum = vnum
        self._unconn = unconn

    # 添加一個頂點
    def add_vertex(self):
        self._mat.append([])
        self._vnum += 1
        return self._vnum - 1

    # 添加一個邊
    def add_edge(self,vi,vj,val=1):
        if self._vnum == 0:
            raise ValueError("Cannot add edge to an empty Graph!")
        if self._invalid(vi) or self._invalid(vj):
            raise ValueError(str(vi) + 'or' + str(vj) + 'is not valid vertex!')

        row = self._mat[vi]
        i = 0
        while i < len(row):
            # 尋找vj對應(yīng)的權(quán)重
            if row[i][0] == vj:
                self._mat[vi][i] = [vj,val]
                return None 
            # 如果沒有找到vj對應(yīng)的值,說明vi和vj不相連
            # 則退出循環(huán)边翁,添加vi與vj的關(guān)聯(lián)邊
            if row[i][0] > vj:
                break
            i += 1
        # 添加vi與vj的關(guān)聯(lián)邊
        self._mat[vi].insert(i,(vj,val))

    def get_edge(self,vi,vj):
        if self._invalid(vi) or self._invalid(vj):
            raise ValueError(str(vi) + 'or' + str(vj) + 'is not valid vertex!')
        
        # 尋找對應(yīng)vi和vj的邊的權(quán)重
        if i,val in self._mat[vi]:
            if i == vj:
                return val
        
        # 如果沒有匹配到啼肩,則返回0
        return self._unconn
#----------圖的壓縮鄰接矩陣實現(xiàn)-----------------



#----------棧的實現(xiàn)---------------------------
class Node:
    def __init__(self):
        self.val = val
        self.next = None

class SStack:
    def __init__(self):
        self.top = None

    # 獲取棧頂?shù)脑?    def peek(self):
        if self.top != None:
            return self.top.val
        else:
            return None

    # 將新元素壓入到棧中
    def push(self,n):
        n = Node(n)
        n.next = self.top
        self.top = n
        return n.val

    # 退出棧
    def pop(self):
        if self.top == None:
            return None
        else:
            tmp = self.top.val
            self.top = self.top.next
            return tmp
#----------棧的實現(xiàn)---------------------------



#----------1. 非遞歸DFS遍歷--------------------
def DFS_graph(graph,v0):
    # 頂點個數(shù)
    vnum = graph.vertex_num()
    # 為每個頂點打上未訪問的標記
    visited = [0] * vnum
    # 遍歷序列
    DFS_seq = [v0]
    # 棧橄妆、用于存儲每個頂點的邊信息
    st = SStack()
    st.push((0,graph.out_edges(v0))) # 壓入與起始結(jié)點相連的邊中的第一條

    while not st.is_empty():
        # 取出頂點序號i與邊列表
        i,edges = st.pop()
        if i < len(edges):
            # v是頂點,e是邊
            v,e = edges[i]
            
            # 壓入與該頂點相連的下一個邊
            # 待前一個邊下面的所有結(jié)點都訪問完了之后再訪問
            st.push((i+1,edges))
            
            # 如果沒有訪問過這個頂點
            if not visited[v]:
                # 將頂點加入列表中
                DFS_seq.append(v)
                # 訪問過之后將標記置為1
                visited[v] = 1

                # 將與搜索到的頂點相連的頂點壓入棧中祈坠,以備下次訪問害碾,這個比163行的更先訪問到
                st.push((0,graph.out_edges(v)))

    return DFS_seq
#----------1. 非遞歸DFS遍歷--------------------



#---------2. DFS生成樹-------------------------
def DFS_span_forest(graph):
    # 頂點個數(shù)
    vnum = graph.vertex_num()
    # 記錄路徑信息
    span_forest = [None] * vnum

    def dfs(graph,v):
        # 需要修改非局部變量,所以將之聲明為nonlocal
        nonlocal span_forest
        # 遍歷頂點v連接的所有路徑赦拘,u是頂點慌随,w是權(quán)重
        for u,w in graph.out_edges(v):
            # 如果這個頂點尚未加入span_forest
            if span_forest[u] is None:
                # 將結(jié)點u的上一結(jié)點和連接權(quán)重記錄下來
                span_forest[u] = (v,w)
                dfs(graph,u)
    
    # 避免還有不連通的點,所以要遍歷到每個頂點
    for v in range(vnum):
        if span_forest[v] is None:
            span_forest[v] = [v,0]
            dfs(graph,v)
    return span_forest            
#---------2. DFS生成樹-------------------------



#---------3. Kruskal最小生成樹-----------------
def Kruskal(graph):
    # 基本思路:設(shè)G = (V,E) 是一個網(wǎng)絡(luò)躺同,其中|V|(頂點數(shù)量)為n阁猜,則Kruskal的基本思路為:
    # (1)取包含G的所有n個頂點但是沒有任何邊的孤立點子圖T = (V,{})
    #     T中的每個頂點自稱一個連通分量,下面將通過不斷擴充T的方式構(gòu)造G的最小生成樹蹋艺;
    # (2)將邊集E按照權(quán)值遞增的順序排序剃袍,在構(gòu)造中的每一步按順序地檢查這個邊序列
    #     找到最短的且兩端位于T的兩個不同的連通分量的邊e,將e加入T捎谨,這樣兩個連通分量
    #     就在e的作用下連接成了一個連通分量民效;
    # (3)每次操作使T減少一個連通分量憔维。不斷重復這個動作,往T中加入新邊畏邢,直到T中所有頂點
    #     都包含在一個連通分量為止业扒,這個連通分量就是G的一棵最小生成樹。

    # 獲取頂點個數(shù)
    vnum = graph.vertex_num()
    # 將各頂點的代表元初始化舒萎,代表元代表了頂點所屬的連通分量
    reps = [i for i in range(vnum)]
    # mst記錄最小生成樹的邊程储,edges記錄graph所有的邊
    mst,edges = [],[]
    # 將所有的邊加入edges
    for vi in range(vnum):
        for v,w in graph.out_edges(vi):
            edges.append((w,vi,v))
    # 按權(quán)重排列
    edges.sort()
    for w,vi,vj in edges:
        # 如果vi和vj屬于兩個不同的連通分量
        if reps[vi] != reps[vj]:
            mst.append(((vi,vj),w))
            
            # 如果有n-1條邊代表已經(jīng)構(gòu)造完成
            if len(mst) == vnum - 1:
                break

            # 修改代表元,將所有與vj在同一連通分量的頂點歸入vi所在的連通分量中            
            rep,orep = reps[vi],reps[vj]
            for i in range(vnum):
                if reps[i] == orep:
                    reps[i] = rep
    return mst
#---------3. Kruskal最小生成樹-----------------

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末臂寝,一起剝皮案震驚了整個濱河市章鲤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌交煞,老刑警劉巖咏窿,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異素征,居然都是意外死亡集嵌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門御毅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來根欧,“玉大人,你說我怎么就攤上這事端蛆》锎郑” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵今豆,是天一觀的道長嫌拣。 經(jīng)常有香客問我,道長呆躲,這世上最難降的妖魔是什么异逐? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮插掂,結(jié)果婚禮上灰瞻,老公的妹妹穿的比我還像新娘。我一直安慰自己辅甥,他們只是感情好酝润,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著璃弄,像睡著了一般要销。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谢揪,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天蕉陋,我揣著相機與錄音捐凭,去河邊找鬼拨扶。 笑死凳鬓,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的患民。 我是一名探鬼主播缩举,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼匹颤!你這毒婦竟也來了仅孩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤印蓖,失蹤者是張志新(化名)和其女友劉穎贬芥,沒想到半個月后之拨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年孵构,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捣辆。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡换况,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厅各,到底是詐尸還是另有隱情镜撩,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布队塘,位于F島的核電站袁梗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏憔古。R本人自食惡果不足惜遮怜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望投放。 院中可真熱鬧奈泪,春花似錦、人聲如沸灸芳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烙样。三九已至冯遂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谒获,已是汗流浹背蛤肌。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工壁却, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裸准。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓展东,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炒俱。 傳聞我的和親對象是個殘疾皇子盐肃,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,936評論 2 89
  • 背景介紹:小程序在iphone 6s、模擬器均運行正常权悟,現(xiàn)在就是在android設(shè)備請求網(wǎng)絡(luò)報錯砸王。 參考資料:ht...
    大胡子的機器人閱讀 1,917評論 0 2
  • 少說話和一些人 別人說的東西要嗎懟回去,要么忍著峦阁,但是自己平常說的話少谦铃,所以自己不敢懟回去,膽小榔昔。所以少說驹闰。要說就...
    三不主義閱讀 117評論 0 0
  • 整個7月,在聽樊登讀書會件豌,從7月回聽到2月疮方,一周一本書。從6?月中旬購買了兩期up子木讀書會茧彤,第六期花了十來天聽完...
    迪菲蘭特閱讀 366評論 0 0