圖論算法

并查集

class UF:
    def __init__(self, n):
        self.parents = list(range(n))
        self.count = n

    def find(self, v):
        if self.parents[v] !=v:
            self.parents[v] = self.find(self.parents[v])
        return self.parents[v]

    def union(self, u, v):
        parent_u = self.find(u)
        parent_v = self.find(v)
        if parent_u != parent_v:
            self.parents[parent_v] = parent_u
            self.count -= 1

    def count(self):
        return self.count

遞歸技巧

鏈接, 在遞歸函數(shù)的開頭奸远,調(diào)用 printIndent(count++) 并打印關(guān)鍵變量劲适;然后在所有 return 語句之前調(diào)用 printIndent(--count) 并打印返回值。

// 全局變量惧笛,記錄遞歸函數(shù)的遞歸層數(shù)
int count = 0;

// 輸入 n葛菇,打印 n 個 tab 縮進(jìn)
void printIndent(int n) {
    for (int i = 0; i < n; i++) {
        printf("   ");
    }
}
# 二叉樹遍歷框架
def traverse(root):
    if not root:
        return
    traverse(root.left);
    traverse(root.right);

# 多叉樹遍歷框架
def traverse(root):
    if not root:
        return
    for child in root.children:
        traverse(child)

# 圖遍歷框架
def traverse(graph, v):
    global visited
    # 防止走回頭路進(jìn)入死循環(huán)
    if visited[v]: 
        return
    # 前序遍歷位置甘磨,標(biāo)記節(jié)點(diǎn) v 已訪問
    visited[v] = True
    for neighbor in graph.neighbors(v):
        traverse(graph, neighbor)

visited = []

# 另一種圖遍歷寫法
visited = []
def traverse(graph: Graph, v: int) -> None:
    # 前序遍歷位置,標(biāo)記節(jié)點(diǎn) v 已訪問
    visited[v] = True
    for neighbor in graph.neighbors(v):
        if not visited[neighbor]:
            # 只遍歷沒標(biāo)記過的相鄰節(jié)點(diǎn)
            traverse(graph, neighbor)

BFS

  • BFS可以找到最短路徑, 但是空間復(fù)雜度高眯停,而 DFS 的空間復(fù)雜度較低
  • 如果知道重終點(diǎn)在哪济舆,可以使用雙向BFS,從終點(diǎn)開始也搜索莺债。時間復(fù)雜度不變滋觉,運(yùn)行速度更快。
from typing import List, Set
from collections import deque

class Node:
    def __init__(self, val: int):
        self.val = val
        self.neighbors = []

def BFS(start: Node, target: Node) -> int:
    q = deque() # 核心數(shù)據(jù)結(jié)構(gòu)
    visited = set() # 避免走回頭路
    q.append(start) # 將起點(diǎn)加入隊(duì)列
    visited.add(start)

    step = 0 # 記錄擴(kuò)散的步數(shù)

    while q:
        step += 1
        size = len(q)
        # 將當(dāng)前隊(duì)列中的所有節(jié)點(diǎn)向四周擴(kuò)散
        for i in range(size):
            cur = q.popleft()
            # 劃重點(diǎn):這里判斷是否到達(dá)終點(diǎn)
            if cur == target:
                return step
            # 將cur相鄰節(jié)點(diǎn)加入隊(duì)列
            for x in cur.neighbors:
                if x not in visited:
                    q.append(x)
                    visited.add(x)
    # 如果走到這里九府,說明在圖中沒有找到目標(biāo)節(jié)點(diǎn)
    return -1

DFS (回溯算法)

多源最短路徑 Floyd

適用于加權(quán)有向圖椎瘟,能夠處理負(fù)權(quán)重的邊,但不能有回路侄旬。
Flyod是處理多源最短路徑(任意 2 點(diǎn))肺蔚,時間復(fù)雜度為O(n^3) 空間復(fù)雜度為O(n ^ 2);
如果是任意兩個點(diǎn)之間的最短路徑或者是負(fù)權(quán)圖,就用Floyd儡羔;

# Floyd-Warshall Algorithm Implementation

# Define a large value for infinity
INF = float('inf')

def floyd_warshall(graph):
    # Number of vertices in the graph
    num_vertices = len(graph)
    
    # Initialize the distance matrix with the same values as the graph matrix
    dist = [[graph[i][j] for j in range(num_vertices)] for i in range(num_vertices)]
    
    # Apply Floyd-Warshall algorithm
    for k in range(num_vertices):
        for i in range(num_vertices):
            for j in range(num_vertices):
                # Update the distance matrix
                if dist[i][j] > dist[i][k] + dist[k][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    
    return dist

# Example usage
graph = [
    [0, 3, INF, INF],
    [2, 0, INF, INF],
    [INF, 7, 0, 1],
    [6, INF, INF, 0]
]

shortest_paths = floyd_warshall(graph)

print("The shortest path matrix is:")
for row in shortest_paths:
    print(row)

單源最短路徑

Dijkstra算法是一種用于計(jì)算單源(從單獨(dú)s到其他任意node t宣羊,或給定某一個 node t)最短路徑的經(jīng)典算法璧诵。它適用于加權(quán)有向圖,但要求所有邊的權(quán)重為非負(fù)數(shù)仇冯。O(N^2)之宿,如果用堆優(yōu)化會更低(O((V+E)logV))。
所以題目中如果是單源點(diǎn)正權(quán)圖苛坚,就用Dijkstra

import heapq

def dijkstra(graph, start, end):
    # Priority queue to store (distance, vertex) tuples
    pq = [(0, start)]
    # Dictionary to store the shortest distance to each vertex
    distances = {vertex: float('inf') for vertex in graph}
    distances[start] = 0
    # Dictionary to store the shortest path to each vertex
    previous_vertices = {vertex: None for vertex in graph}

    while pq:
        # Get the vertex with the smallest distance
        current_distance, current_vertex = heapq.heappop(pq)

        # If we have reached the end vertex, we can stop
        if current_vertex == end:
            break

        # Explore neighbors
        for neighbor, weight in graph[current_vertex].items():
            distance = current_distance + weight

            # If a shorter path to the neighbor is found
            if distance < distances[neighbor]:
                distances[neighbor] = distance
                previous_vertices[neighbor] = current_vertex
                heapq.heappush(pq, (distance, neighbor))

    # Reconstruct the shortest path
    path = []
    current_vertex = end
    while previous_vertices[current_vertex] is not None:
        path.append(current_vertex)
        current_vertex = previous_vertices[current_vertex]
    path.append(start)
    path.reverse()

    return path, distances[end]

# Example usage
graph = {
    'A': {'B': 1, 'C': 4},
    'B': {'A': 1, 'C': 2, 'D': 5},
    'C': {'A': 4, 'B': 2, 'D': 1},
    'D': {'B': 5, 'C': 1}
}

start_node = 'A'
end_node = 'D'
path, distance = dijkstra(graph, start_node, end_node)

print(f"The shortest path from {start_node} to {end_node} is: {path} with distance {distance}")

bellman-ford 算法

更加通用版本的 dijkstra比被,可以處理負(fù)權(quán)重。和dijkstra算法的區(qū)別是不使用小頂堆(本質(zhì)是個貪心)泼舱,B-F是個動態(tài)規(guī)劃等缀,要全部遍歷, 復(fù)雜度是 O(V*E)。

bellman-ford適合稠密的小圖娇昙,或者帶負(fù)權(quán)重的邊尺迂,dijkstra適合稀疏的大圖。

class Graph:
   def __init__(self, vertices):
       self.V = vertices  # 頂點(diǎn)數(shù)
       self.edges = []    # 邊的列表

   def add_edge(self, u, v, weight):
       self.edges.append((u, v, weight))

   def bellman_ford(self, source):
       # 初始化距離
       dist = [float('inf')] * self.V
       dist[source] = 0

       # 松弛所有邊 V-1 次
       for _ in range(self.V - 1):
           for u, v, weight in self.edges:
               if dist[u] != float('inf') and dist[u] + weight < dist[v]:
                   dist[v] = dist[u] + weight

       # 檢測負(fù)權(quán)環(huán)
       for u, v, weight in self.edges:
           if dist[u] != float('inf') and dist[u] + weight < dist[v]:
               print("Graph contains a negative weight cycle")
               return

       # 打印結(jié)果
       for i in range(self.V):
           print(f"Distance from source {source} to vertex {i} is {dist[i]}")

# 示例用法
g = Graph(5)
g.add_edge(0, 1, -1)
g.add_edge(0, 2, 4)
g.add_edge(1, 2, 3)
g.add_edge(1, 3, 2)
g.add_edge(1, 4, 2)
g.add_edge(3, 1, 1)
g.add_edge(3, 2, 5)
g.add_edge(4, 3, -3)

g.bellman_ford(0)

最小生成樹

kruskal算法

并查集基礎(chǔ)上冒掌,對edge排序噪裕,每次加入weight最小的,不連通的邊股毫。

prim算法

prim

切分定理:
對于任意一種「切分」膳音,其中權(quán)重最小的那條「橫切邊」一定是構(gòu)成最小生成樹的一條邊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末皇拣,一起剝皮案震驚了整個濱河市严蓖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌氧急,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毫深,死亡現(xiàn)場離奇詭異吩坝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)哑蔫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門钉寝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人闸迷,你說我怎么就攤上這事嵌纲。” “怎么了腥沽?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵逮走,是天一觀的道長。 經(jīng)常有香客問我今阳,道長师溅,這世上最難降的妖魔是什么茅信? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮墓臭,結(jié)果婚禮上蘸鲸,老公的妹妹穿的比我還像新娘。我一直安慰自己窿锉,他們只是感情好酌摇,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嗡载,像睡著了一般窑多。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鼻疮,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天怯伊,我揣著相機(jī)與錄音,去河邊找鬼判沟。 笑死耿芹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的挪哄。 我是一名探鬼主播吧秕,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼迹炼!你這毒婦竟也來了砸彬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤斯入,失蹤者是張志新(化名)和其女友劉穎砂碉,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刻两,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡增蹭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了磅摹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滋迈。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖户誓,靈堂內(nèi)的尸體忽然破棺而出饼灿,到底是詐尸還是另有隱情,我是刑警寧澤帝美,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布碍彭,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏硕旗。R本人自食惡果不足惜窗骑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漆枚。 院中可真熱鬧创译,春花似錦、人聲如沸墙基。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽残制。三九已至立砸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間初茶,已是汗流浹背颗祝。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恼布,地道東北人螺戳。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像折汞,于是被迫代替她去往敵國和親倔幼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

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

  • 本來下學(xué)期在學(xué)姐的強(qiáng)力安利之下選了算法這個課爽待,但是取消了损同,于是在家打發(fā)時間看了edX上的一個法國人講的算法網(wǎng)課。主...
    當(dāng)年老子最帥閱讀 3,662評論 0 3
  • 一個圖(graph) G=(V, E)由頂點(diǎn)(vertex) 的集 V 和邊(edge) 的集 E 組成鸟款。每一條邊...
    Sun東輝閱讀 725評論 0 3
  • 一膏燃、并查集?并查集,在一些有N個元素的集合應(yīng)用問題中何什,我們通常是在開始時讓每個元素構(gòu)成一個單元素的集合蹄梢,然后按一定...
    肖一二三四閱讀 1,301評論 0 0
  • 1. 圖的表示:鄰接矩陣和鄰接表 鄰接矩陣:大小為|V|的二維數(shù)組,對于每條邊(u, v)富俄,置A[u][v]=1或...
    第八天的蟬啊閱讀 221評論 0 0
  • 深度優(yōu)先搜索 在圖中搜索的一般過程為: 記錄當(dāng)前結(jié)點(diǎn)被發(fā)現(xiàn)的時間(discovery time) 遍歷訪問未被訪問...
    njzwj閱讀 1,756評論 0 0