資料結構(python版)

Roy2048發表於2024-05-26

資料結構與演算法


python佇列queue

詳見python3自定義比較器

python比較器

Python heapq 自定義比較器

#自定義比較器
#1. 對list等有key引數的
##二維陣列等的比較排序
list1.sort(key = lambda x: x[1])
##list中放置其他資料型別
import functools
#cmp的返回值為負數,第一個數在第二個數前面
#cmp的返回值為正數,第二個數在第一個數前面
def cmp(s1, s2):
    pass
list1.sort(key = functools.cmp_to_key(cmp))
#2. 佇列操作
import queue
q = queue.Queue()
q.put()
q.get()
#3.優先順序佇列
qp = queue.Priorityqueue()
qp.put()#通常為一個(2,)的資料,第一位為優先順序,越小越優先,第二位為放入的資料項
#4.堆的操作
import heapq
heap = []
heapq.heappush(heap, list_data)#第一種把heap初始化為小根堆
heapq.heapify(list_data)#第二種
heapq.heappop(0)#彈出根節點

四大邏輯結構:集合結構,線性結構,樹形結構,圖形結構

物理結構:順序儲存(地址連續), 鏈式儲存(更靈活)

演算法效率

O(1) < O(logn) < O(n) < O(nlogn) < O(n^2)

1. 排序

選擇排序

思想: 依次找到最小值放到最前面 時間複雜度O(n^2),空間複雜度O(1)

//(0 ----N-1)位置找到min放到0位置
//(1------N-1) 位置找到min放到1位置

氣泡排序

思想:(0 ---N-1)相鄰兩個比較,max往右移動;(0--N-2)相鄰兩個比較,max右移

// 1 2 3 4 5 6
// 1 2 位置誰大誰往右移
//   2 3 位置上誰大誰往右移
//     3 4 位置上誰大誰往右移

插入排序

思想:保證0-1有序,保證0-2有序,保證0-3有序,如果i位置比i-1位置小,交換,直到i不比i-1位置小。

def xuanze(arr):#選擇排序
    for i in range(len(arr)):
        for j in range(i+1, len(arr)):
           # print(i)
            if arr[j] < arr[i]:
                arr[i], arr[j] = arr[j],arr[i]
    return arr
def maopao(arr):#氣泡排序
    for i in range(len(arr)):
        for j in range(len(arr) -1-i):
            if arr[j] > arr[j+1]:
                arr[j+1], arr[j] = arr[j], arr[j+1]
    return arr
def charu(arr):#插入排序
    for i in range(1, len(arr)):
        now = i
        for j in range(i-1, -1, -1):
            global bigo
            bigo += 1
            if arr[now] < arr[j]:
                print(str(now)+'位置換到'+str(j)+'位置去')
                arr[now], ar r[j] = arr[j], arr[now]
                now = j
            else:
                break

global bigo#用於檢視到底進行了幾次
bigo = 0
arr = [1,2,3,4,6,0]
print(arr)
charu(arr)
print(arr)
print(bigo)

二分(logn)

遞迴做

arr = [1,2,3,4,5,6]
def erfeng(arr, num, L, R):
    print('again')
    if L == R:
        pass
    mid = int(L +(R-L)/2)
    if num < arr[mid]:
        R = mid
        erfeng(arr, num, L, R)
    elif num > arr[mid]:
        L = mid +1
        erfeng(arr,num, L,R)
    else:
        print('zhaodaol')
        print(arr[mid])

L = 0
R = len(arr)
erfeng(arr, 1, L, R)

有序數列找比x小的最左側數

arr = [1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,4,4,4,4,4,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6]
def erfengzuo(arr, num, L, R, t):#first, t = R
    print('now is %d, %d'%(L, R))
    mid = int(L + (R - L)/2)
    if arr[mid] >= num:
        if L == mid:
            raise IndexError
        if t> mid:
            t = mid
        R = mid
        erfengzuo(arr, num, L, R, t)
    elif arr[mid]< num:
        L = mid +1
        erfengzuo(arr, num, L, R, t)
    else:
        print(arr[mid])
erfengzuo(arr, 3,0, len(arr), len(arr))    

還有 無序陣列上找區域性最小的問題(畫出來,思維很簡單)

對數器 可以提供類似OJ的效果

random() [0,1) 等機率返回一個小數

int(random()*N) 等機率返回一個[0, N) 上的隨機整數

快速排序(荷蘭國旗問題)

給定陣列arr, 和一個數num,把小於num的數放在陣列左邊,大於num的數放在陣列右邊,時間複雜度為O(n),空間複雜度為O(1).


升級: 一個陣列,左邊小於num, 中間== num, 右邊大於num

思維誤區:三個指標, 小於區的右邊界,等於區的右邊界*** 等於區的右邊界其實是大於區的左邊界***

時間複雜度O(N * logn), 空間複雜度O(logn)

import random
def kuaisu(arr, L, R):
    if L< R:   #注意判斷條件!!!使用(2,2,3)試一試 
        random_num = int(random.random()*(len(arr)-1))#轉化為int型別!!!
        print('前arr%s'% arr)
        num = arr[random_num]
        print('num: arr的第%d 個數,num為%d' % (random_num, num))
        small, big = equalok(arr,num,L, R)
        print('small:%d; big:%d'% (small, big))
        print('L    :%d; R  :%d'%(L, R))
        print('後arr%s'% arr)

        kuaisu(arr, L, small)
        kuaisu(arr, big, R)


def equalok(arr, num, L, R):    
    small = L-1#注意邊界問題i的取值是L開始的
    big = R +1
    i = L
    while(i != big):
        if arr[i]< num:
            arr[i], arr[small +1] = arr[small +1], arr[i]
            i += 1
            small += 1
        elif arr[i]> num:
            arr[i], arr[big -1] = arr[big-1], arr[i]
            big -= 1
        elif arr[i] == num:
            i +=1
    return small, big
arr = [1,23,3,4,2]
kuaisu(arr, 0, len(arr)-1)
print(arr)

2. 遞迴

master公式 T(N) = a* T(N/b) + o(N^d)

while : a 子問題呼叫次數, N/b 子問題規模, o(N^d) 處理遞迴外的其他複雜度

log(b,a)< d 為O(N^d)

log(b, a) > d 為 O(N^(log(a,b)))

log(b, a) = d 為O( N^d * logn)

歸併排序

經典問題

時間複雜度 : O(N * logN)

注意點:如果a = [0,1], mid = 0, 列印a[L, mid] 結果為None

def merge(arr, L, R):

    if L+1 == R:
        print(L, end = ' ')
        print(R)
        if arr[L] > arr[R]:
            arr[L], arr[R] = arr[R], arr[L]

        return arr
    elif L ==R:#注意對只有1個和2個元素時的處理方式
        return arr
    else:
        mid = int(L + (R-L)/2)#mid一定要轉為整形

        merge(arr, L, mid)
        merge(arr, mid +1, R)

        arr[L: R+1] = togother(arr, L, R, mid)
        return arr

def togother(arr, L, R, mid):
    p1 = L
    p2 = mid+1
    help1 = []
    while( p1< mid +1 and p2 < R +1):#注意剩餘幾個元素的拉去條件
        if arr[p1]< arr[p2]:
            help1.append(arr[p1])
            p1 += 1
        else:
            help1.append(arr[p2])
            p2 += 1
    if p1 == mid+1 :
        help1.extend(list(arr[p2:R+1]))
    if p2 == R+1:
        help1.extend(list(arr[p1:mid+1]))

    return help1
arr = [1,3,6,4,9,4,0,23,3]
arr = merge(arr, 0, len(arr)-1)
print(arr)  

求小和問題

( 1,3,4,2,5)求每個數左邊比這個數小的和

:在一個陣列中,每一個數左邊比當前數小的數累加起來,叫做這個陣列的小和

注意點:思維轉化,把比某個數左邊小的數總和,變為某個數右邊有多少個比它大的


逆序對問題:在一個陣列中,左邊的數如果比右邊的數大,則這兩個數構成一個逆序對。求所有的逆序對。

思維同樣轉換為上述。


小和問題程式碼:

#1.134和25合併時,左邊的小和已經計算過了,所以只需要管右邊的就可以
#     某個數不會和自己所在的組產生小和;
#2. 當左右資料相同時,先把右邊的merge進去。
global he
he = 0
def xiaohe(arr, L, R):
    if L + 1 == R:
        if arr[L]> arr[R]:
            arr[L], arr[R] = arr[R], arr[L]
        elif arr[L] == arr[R]:
            pass
        else:
            global he
            he += arr[L]
            print('two merge:%d'% he)
        return arr
    elif L == R:
        return arr
    else:
        mid = int(L + (R-L)>>1)

        xiaohe(arr, L, mid)
        xiaohe(arr, mid+1, R)
        print('left : %s'% arr[L:mid+1], end = ' ')
        print('right: %s'% arr[mid+1: R+1])
        arr[L:R+1] = togother(arr, L, mid, R)
        print('merge: %s' % arr[L:R+1])
    return arr

def togother(arr, L, mid,R):
    p1 = L
    p2 = mid+1
    help1 = []
    while(p1<mid+1 and p2< R+1):
        if arr[p1]<arr[p2]:
            help1.append(arr[p1])

            global he
            he += (arr[p1] * (R - p2+1))
            print('p1: %d, p2:%d, 乘數:%d'%(p1, p2, (R-p2 +1)))
            print('all merge: %d'% he)
            p1 +=1
        else: #if arr[p1]>=arr[p2]:
            help1.append(arr[p2])
            p2 += 1
    while(p1 == mid +1) :
        help1.extend(list(arr[p2:R+1]))
        p1 += 1
        print('?')
    while(p2 == R +1):
        help1.extend(list(arr[p1: mid +1]))
        p2 += 1
        print('?')

    return help1

arr = [1,3,4,2,5]
arr = xiaohe(arr, 0, len(arr)-1)
print(he)
print(arr)

3. 堆

堆結構就是用陣列實現的完全二叉樹結構,堆中的某個節點總是不小於或不大於其父節點的值。

一個陣列從0開始,連續的一段可以構成完全二叉樹


特性 :1.連續的長度用size表示

  1. i位置的左孩子為: 2 * i +1; i 位置的右孩子為 2 * i + 2;當 這個值比size小時;
  2. i 位置的父節點為: int((i -1)/ 2 ) # 包括0節點

大根堆:完全二叉樹中,如果每棵子樹的最大值都在頂部;小根堆:完全二叉樹的每棵子樹的最小值都在頂部

heapinsert操作和 heapify操作,以及堆排序

堆排序思想:

  1. 把陣列arr變成一個堆
  2. 把跟節點和最後一個數交換,pop出來,heapsize --, 直到heapsize = 0.

時間複雜度: [........|........],如果有2N個元素,後面N個排序最差需要logN次, 共N個,所以是N*(logn)

#堆排序和兩個操作
def heapinsert(arr, index):
    while(arr[index]> arr[int((index -1)/2)]):
        arr[index], arr[int((index - 1)/2)] = arr[int((index -1)/2)], arr[index]
        index = int((index -1)/2)

def heapify(arr, index, heapsize):
    left = 2 * index + 1
    while(left < heapsize):
        max_num = left+1 if left+1 < heapsize and arr[left + 1] > arr[left] else left
        max_num = max_num if arr[index] < arr[max_num] else index
        if (max_num == index):
            break
        arr[index], arr[max_num] = arr[max_num], arr[index]
        index = max_num
        left = 2 * index +1

def heapsort(arr):
    for i in range(len(arr)-1):
        heapinsert(arr, i)
    print(arr)
    heapsize = len(arr)
    arr[0], arr[len(arr) - 1] = arr[len(arr) - 1], arr[0]
    heapsize -=1
    heapify(arr, 0, heapsize)

    while(heapsize > 0):
        arr[0], arr[heapsize - 1] = arr[heapsize - 1], arr[0]
        heapsize -= 1
        heapify(arr, 0, heapsize)


arr = [1,3,2,6,5,8,3,5]
heapsort(arr)
print(arr)

擴充套件

已知一個幾乎有序的陣列,(每個元素的移動距離不超過k)選擇

import heapq#直接使用以及有的庫,庫預設為小根堆
def distancelessk(arr, k):
    heap = []
    for i in range(k+1):
        heapq.heappush(heap, arr[i])
    index = 0
    while(index+k +1 < len(arr)):
        arr[index] = heapq.heappop(heap)
        heapq.heappush(heap, arr[index+k + 1])
        index += 1
    for i in range(k+1):
        arr[index] = heapq.heappop(heap)
        #print(arr[index])
        index += 1
arr= [2,3,1,5,6,4,7,9,8]   
distancelessk(arr, 2)
print(arr)

heapq中 大根堆的實現:heappush()的時候,填入資料*-1, heappop()的時候, 出來的資料 * -1

比較器

比較器的實質就是過載比較運算子

詳見python3自定義比較器

python比較器

自定義比較器返回值為負數, 第一個引數在前面

返回值為正數,第二個數排前面(升序排列)自己修改

在sort函式中新增排序的策略,可以實現自己的比較器


之前的排序都是基於兩個數的比較進行的

不基於比較的排序:只有很窄的應用範圍,

4. 桶排序

不基於比較的排序,有條件限制

記數排序

eg_-1: 員工年齡排序,構建一個[16, ....., 100]的陣列,遇到某個歲數,某個歲數++,遍歷一遍後,得到員工年齡詞頻統計表, 得到排序。

基數排序[17, 13, 25, 100, 72]

補全最高位的0, 按照最後一位數字進桶(桶是佇列,先進先出)

匯出桶中數字:

[100, 072, 013, 025, 017]

按照第二位進桶

0: 100,

1:013, 017

2:025

7:072

出桶: [100, 013, 017, 025 ,072]

按照第三位進桶:

0:013, 017, 024, 072

1: 100

出桶: [13, 17, 24, 7, 100]

5. 排序演算法彙總

排序穩定性: 指相同的元素再每次排序後是否能保持相同的位置(相同元素的相對次序)

eg: arr1[]:對商品價格從小到大排序

​ arr2[]: 基於上述排序對好評率從小到大排序,若能保持上述資料的排序基礎,則很有價值(體現排序的穩定性)

6個排序穩定性: 堆排,快排

快排最常用(常數項最低,所以最快一般),有空間要求選堆排,有穩定性要求選歸併

  1. 基於比較的時間複雜度最低N*logn
  2. 時間複雜度最低,空間複雜度小於N,則不能實現穩定性

排序的改進:

  1. 大規模時候採用N*logn的排序排程演算法,小範圍用N^2的演算法比較快
  2. 穩定性考慮

6. 雜湊

  1. 如果只有Key,沒有value,使用Hashset結構;key-value對,用hashmap結構
  2. 雜湊操作複雜度為常數項,但常數比較大。
  3. key值為基礎型別(int,string...),則直接複製;key值為自定義型別(類),則為自定義型別的記憶體地址

7. 連結串列

題目:

  1. 單連結串列雙連結串列 反轉(換頭帶返 回值,否則不帶)

  2. 列印兩個有序連結串列的公共部分,給定兩個有序連結串列的頭指標h1,h2;

技巧: 1. 使用額外資料記錄結構(雜湊表);2. 快慢指標

  1. 判斷連結串列是否為迴文結構(前後對稱):遍歷進棧,pop的時候和原連結串列比較

  2. 給定一個單連結串列head,整數型別,給一個數pivot,將連結串列調整為做部分小於pivot,中間等於,右邊小於。

  3. 複製含有隨機指標節點的連結串列

  4. 兩個連結串列相交問題:給定兩個可能有環也可能無環的單連結串列,頭節點head1,head2,實現一個函式,如果兩個連結串列相交,返回相交的第一個節點,如果不相交,返回null

8. 二叉樹

class Node:
    V value
    Node left
    Node right
  1. 用遞迴和非遞迴兩種方式實現二叉樹的先序、中序、後序遍歷,

  2. 直觀的列印二叉樹

  3. 完成二叉樹的寬度優先遍歷(求一顆二叉樹的寬度)

遞迴序:

遞迴的核心程式碼:

def recur(Node):
    if (Node == None):
        return
    #第一次到該節點的操作程式碼
    recur(Node.left)
    #第二次回到該節點所執行的程式碼
    recur(Node.right)
    #第三次回到該節點所執行的程式碼

要點 : 遞迴

遞迴序的基礎上,可以加工出先序、中序、後序。(順序是對於每個子樹都是這樣)

先序(頭,左,右): 列印頭節點,列印左節點,列印右節點(在遞迴演算法中第一次列印)

中序(左,頭,右):4,2,5,1,6,3,7(在遞迴演算法中第二次列印,第一次和第三次都不列印)

後序(左,右,頭):4, 5,2,6,7,3,1(在遞迴演算法中第三次列印,第一次和第二次都不列印)

非遞迴序

任何遞迴都可以改成非遞迴函式

自己準備一個棧

先序遍歷

後續遍歷

壓棧的時候先壓入左,後右,得到頭左右的順序,入棧出棧得到左右頭的後續遍歷

中序遍歷

思想

  1. 整顆樹左邊進棧
  2. 依次彈出的過程中,列印
  3. 對彈出節點的右節點依次操作

求二叉樹的寬度(完成二叉樹寬度的優先遍歷)

寬度優先遍歷

的基礎上操作

二叉樹的相關問題

  1. 搜尋二叉樹判斷:左樹的節點比根節點小,右樹的節點比根節點大

判斷:中序遍歷是升序,是搜尋二叉樹

  1. 完全二叉樹判斷:
  2. 滿二叉樹判斷
  3. 平衡二叉樹判斷:對任何一個子樹,左右樹的高度差不超過1

要左樹,要右數的遞迴套路

  1. 給定兩個二叉樹節點node1和node2, 找到他們的最低公共祖先節點
  2. 二叉樹的序列化和反序列化:記憶體裡的一棵樹變成字串形式/字串變成記憶體裡的二叉樹
  3. 如何判斷一個二叉樹是不是另一個二叉樹的子樹

9. 圖

1. 圖的儲存方式

  1. 鄰接表

點集作為單位:

  1. 鄰接矩陣(一定是個正方形)

2. graph

class Graph:
    def __init__(self):
        self.nodes = {}#點集  點序號:Node
        self.edges = set()#邊集

class Edges:#邊
    def __init__(self, weight, from_node, to_node):
        self.weight = weight
        self.from_node = from_node
        self.to_node = to_node
class Node:
    def __init__(self, value):
        self.value = value
        self.in_number = 0#入度
        self.out_number = 0#出度
        self.nexts = []#存連線的Node列表
        self.edges = []#存發出的邊的列表      

3. 寫介面

def transfer(matrix):
    new_graph = Graph()#新建一個圖
    for row in matrix:
        from_node = row[0]
        to_node = row[1]
        weight = row[2]
        #看from和to節點是不是已經存在與圖中
        #新增節點到圖的nodes裡
        if from_node not in new_graph.nodes.keys():
            new_graph.nodes[from_node] = Node(from_node)
        if to_node not in new_graph.nodes.keys():
            new_graph.nodes[to_node] = Node(to_node)
        #新增邊到edges裡    
        new_edge = Edge(weight, from_node, to_node)
        new_graph.edges.add(new_edge)
        #改變node中的其他值
        new_graph.nodes[from_node].out_number += 1
        new_graph.nodes[from_node].edges.append(new_edge)
        new_graph.nodes[from_node].nexts.append(new_graph.nodes[to_node])
        new_graph.nodes[to_node].in_number +=1
        #驗證
        '''
        print('node %d in dict, value %d, in_number %d, out_number %d, nextsadd: %d, edgesweight: %d'\
             %(from_node, new_graph.nodes[from_node].value, new_graph.nodes[from_node].in_number\
              , new_graph.nodes[from_node].out_number, new_graph.nodes[from_node].nexts[-1].value,\
              new_graph.nodes[from_node].edges[-1].weight))
        '''
    return new_graph

matrix = [[1,2,4],[2,3,3],[3,1,7],[3,2,3],[4,1,2]]
G1 = transfer(matrix)

4. 圖的寬度優先遍歷

非常重要: 在新增node.nexts時判斷是否註冊過

import queue
def bfs(node):
    if node.value == None:
        return
    q = queue.Queue()#別忘了括號
    register = set()#登錄檔,防止環狀

    q.put(node)#進佇列
    register.add(node)#註冊

    while(not q.empty()):#佇列不空時
        cur = q.get()
        print(cur.value)#替換別的操作
        for anode in cur.nexts:#每次進nexts時,新增的nexts都在佇列最後面,所以這一層走完才可以走下一層,即寬度優先
            if anode not in register:#把沒有註冊的nodes裡面的node進佇列,並註冊
                   q.put(anode)
                register.add(anode)

matrix = [['A','B',0],['A','C',0],['A','E',0],['B','C',0],['C','E',0],['B','D',0]\
         ,['B','A',0],['C','A',0],['E','A',0],['C','B',0],['E','C',0],['D','B',0]]
G2 = transfer(matrix)
bfs(G2.nodes['A'])

5. 圖的廣度優先遍歷

Important: 1. 每一層只入棧一個元素,需要break;2. 元素的處理位置在第一次入棧的時候,因為會出入棧好幾次。

def dfs(node):
    if node.value == None:
        return 
    stack = []
    register = set()

    stack.append(node)
    register.add(node)
    print(node.value)#important

    while(stack):
        cur = stack.pop()
        #print(cur.value)

        for anode in cur.nexts:
            if anode not in register:
                stack.append(cur)
                stack.append(anode)
                register.add(anode)
                print(anode.value)#important
                break#most important step,在這一層中只看一個node,然後cur也進棧
                #記住處理元素的位置,node出棧次數超過一次,所以在第一次進棧的時候處理

6.拓撲排序

#拓撲排序(編譯器順序,先編譯入度為0的包,去除這個包和所帶來的影響,再找入度為0的包編譯)
#要求:單向圖,且有0入度的節點,沒有環

import queue
def sortedtopology(graph):
    Inmap = {}#用來儲存node:in_number,來判斷要進入佇列的節點
    zero_in_queue = queue.Queue()
    for node_key, node in graph.nodes.items():#生成Inmap,同時找出此時入度為0的節點放入佇列
        Inmap[node] = node.in_number
        if node.in_number == 0:
            zero_in_queue.put(node)
    result = []        #要給出的列表
    while(not zero_in_queue.empty()):#佇列不空時,取一個放入返回列表
        cur = zero_in_queue.get()
        result.append(cur)

        for anode in cur.nexts:#抹除當前節點對剩餘序列的影響
            Inmap[anode] -= 1
            if Inmap[anode] == 0:
                zero_in_queue.put(anode)
    return result

matrix2 = [['d','b',0], ['e', 'b',0],['c','b',0],['e','a',0], ['c','a',0],['b','a',0]]
G2 = transfer(matrix2)
result = sortedtopology(G2)
for node in result:
    print(node.value)

7. 最小生成樹演算法

1. 並查集

class Mysets:
    def __init__(self, nodes):
        self.mysets = {}# 字典 node:set
        for node in nodes.keys():
            self.mysets[nodes[node]] = {nodes[node]}#node類:node類的集合
     #提供兩個函式,一個判斷是否連線,一個用來結合起來       
    def is_same_set(self, from_node, to_node):
        from_set = self.mysets[from_node]
        to_set = self.mysets[to_node]
        return from_set == to_set#判斷是不是相同

    def union(self, from_node, to_node):
        from_set = self.mysets[from_node]
        to_set = self.mysets[to_node]
        for node in to_set:#把to_set裡的節點新增到fromset裡去,讓to_node的值和from_set相同
            from_set.add(node)
            #self.mysets[to_node] = from_set
            self.mysets[node] = from_set ##most important :把to_set中的node的to_set都改為from_set
'''
測試資料
'''
matrix3 = [['a','b',3],['a','c',100],['a','d',7],['c','a',100],['d','a',7],['b','a',3],\
          ['c','b',5],['b','c',5],['c','d',100],['d','c',100],['b','d',2],['d','b',2]]
G3 = transfer(matrix3)
mysets = Mysets(G3.nodes)
'''
測試用例
'''
for i in myset.mysets:
    for j in myset.mysets[i]:
        print(j.value)
myset.union(G3.nodes['a'],G3.nodes['b'])
for node_set in myset.mysets:
    print('---------')
    for node in myset.mysets[node_set]:
        print(node.value)
    print('---------')

print(myset.is_same_loop(G3.nodes['a'], G3.nodes['c']))

2. K演算法(邊思想)


思想:

從最小邊開始判斷,新增並檢視是不是有環

  1. 對每個節點初始化一個set集合,只有自己在裡面
  2. 對所有的邊從小到大排序,從最小的邊開始遍歷
  3. 利用並查集判斷from節點和to節點的set是否相同
    如果不同,把邊新增到results中,
    from節點的set中新增toset中的所有資料
    toset中的所有節點賦值為from節點所指向的set

def k_algorithm(graph):
    results = []
    mysets = Mysets(graph.nodes)
    #判斷初始化是否成功
    for node, node_set in mysets.mysets.items():
        print('node:%s,set:'%(node.value), end = '')

        for i in node_set:
            print(' %s '%(i.value), end = ' ')
        print('')

    list_edges = list(graph.edges)
    list_edges.sort(key = lambda x:x.weight)

    for edge in list_edges:
        print('-----------new loop------------------')
        print('判斷邊edge: from %s, to %s, weight %d'%(edge.from_node, edge.to_node, edge.weight))
        from_node = graph.nodes[edge.from_node]
        to_node = graph.nodes[edge.to_node]
        if not mysets.is_same_set(from_node, to_node):
            results.append(edge)
            mysets.union(from_node, to_node)
        #判斷loop之後的是否成功
        for node, node_set in mysets.mysets.items():
            print('node:%s,set:'%(node.value), end = '')

            for i in node_set:
                print(' %s '%(i.value), end = ' ')
            print('')
        #判斷result是否正確
        print('此時的results為:')
        for edge in results:
            print('edge: from %s, to %s, weight %d'%(edge.from_node, \
                                                     edge.to_node, edge.weight), end = ' ')
        print('')
    return results

#測試資料
lujing = k_algorithm(G3)
for i in lujing:
    print('from %s, to %s, weight %d'%(i.from_node, i.to_node, i.weight))

3. P演算法(點思想)


1.隨機選擇一個點開始,註冊,在’邊‘的優先佇列中新增所有的邊進去,

2.當優先佇列不為空時,取出當前最小邊,判斷tonode沒有註冊,註冊,邊放入results,把tonode的所有邊放入佇列

import queue
def p_algorithm(graph):
    node_register = set()
    edge_priorityqueue = queue.PriorityQueue()
    results = set()

    for node_name in graph.nodes.keys():#解決森林問題,即有些圖不是連通圖。
        node = graph.nodes[node_name]
        node_register.add(node)
        for edge in node.edges:
            edge_priorityqueue.put((edge.weight, id(edge),edge))#當第一項相同時,第二項不相同,most important
                                                                #否則報錯edge和edge沒有實現比較方法
        while(not edge_priorityqueue.empty()):
            edge = edge_priorityqueue.get()[2]
            #print(edge.weight)
            to_node_name = edge.to_node
            if graph.nodes[to_node_name] not in node_register:
                node_register.add(graph.nodes[to_node_name])
                results.add(edge)
                for smalledge in graph.nodes[to_node_name].edges:
                    edge_priorityqueue.put((smalledge.weight,id(smalledge), smalledge))
        break#沒有森林時使用
    return results

results = p_algorithm(G3)                
for i in results:
    print(i.weight)

8. Dijkstra演算法

規定一個出發點,算出出發點到每一個節點的最短路徑

適用範圍:沒有權值為負數的邊

10. 字首樹

相關文章