全網最適合入門的物件導向程式設計教程:45 Python 實現常見資料結構-連結串列、樹、雜湊表、圖和堆

FreakStudio發表於2024-09-05

全網最適合入門的物件導向程式設計教程:45 Python 實現常見資料結構-連結串列、樹、雜湊表、圖和堆

image

摘要:

資料結構是電腦科學中的一種組織和儲存資料的方式,它決定了資料的訪問方式和操作效率,資料結構的選擇和實現對程式的效能和設計至關重要。本文主要講述瞭如何使用 Python 語言和內建庫實現常見資料結構。

原文連結:

FreakStudio的部落格

往期推薦:

學嵌入式的你,還不會物件導向??!

全網最適合入門的物件導向程式設計教程:00 物件導向設計方法導論

全網最適合入門的物件導向程式設計教程:01 物件導向程式設計的基本概念

全網最適合入門的物件導向程式設計教程:02 類和物件的 Python 實現-使用 Python 建立類

全網最適合入門的物件導向程式設計教程:03 類和物件的 Python 實現-為自定義類新增屬性

全網最適合入門的物件導向程式設計教程:04 類和物件的Python實現-為自定義類新增方法

全網最適合入門的物件導向程式設計教程:05 類和物件的Python實現-PyCharm程式碼標籤

全網最適合入門的物件導向程式設計教程:06 類和物件的Python實現-自定義類的資料封裝

全網最適合入門的物件導向程式設計教程:07 類和物件的Python實現-型別註解

全網最適合入門的物件導向程式設計教程:08 類和物件的Python實現-@property裝飾器

全網最適合入門的物件導向程式設計教程:09 類和物件的Python實現-類之間的關係

全網最適合入門的物件導向程式設計教程:10 類和物件的Python實現-類的繼承和里氏替換原則

全網最適合入門的物件導向程式設計教程:11 類和物件的Python實現-子類呼叫父類方法

全網最適合入門的物件導向程式設計教程:12 類和物件的Python實現-Python使用logging模組輸出程式執行日誌

全網最適合入門的物件導向程式設計教程:13 類和物件的Python實現-視覺化閱讀程式碼神器Sourcetrail的安裝使用

全網最適合入門的物件導向程式設計教程:全網最適合入門的物件導向程式設計教程:14 類和物件的Python實現-類的靜態方法和類方法

全網最適合入門的物件導向程式設計教程:15 類和物件的 Python 實現-__slots__魔法方法

全網最適合入門的物件導向程式設計教程:16 類和物件的Python實現-多型、方法重寫與開閉原則

全網最適合入門的物件導向程式設計教程:17 類和物件的Python實現-鴨子型別與“file-like object“

全網最適合入門的物件導向程式設計教程:18 類和物件的Python實現-多重繼承與PyQtGraph串列埠資料繪製曲線圖

全網最適合入門的物件導向程式設計教程:19 類和物件的 Python 實現-使用 PyCharm 自動生成檔案註釋和函式註釋

全網最適合入門的物件導向程式設計教程:20 類和物件的Python實現-組合關係的實現與CSV檔案儲存

全網最適合入門的物件導向程式設計教程:21 類和物件的Python實現-多檔案的組織:模組module和包package

全網最適合入門的物件導向程式設計教程:22 類和物件的Python實現-異常和語法錯誤

全網最適合入門的物件導向程式設計教程:23 類和物件的Python實現-丟擲異常

全網最適合入門的物件導向程式設計教程:24 類和物件的Python實現-異常的捕獲與處理

全網最適合入門的物件導向程式設計教程:25 類和物件的Python實現-Python判斷輸入資料型別

全網最適合入門的物件導向程式設計教程:26 類和物件的Python實現-上下文管理器和with語句

全網最適合入門的物件導向程式設計教程:27 類和物件的Python實現-Python中異常層級與自定義異常類的實現

全網最適合入門的物件導向程式設計教程:28 類和物件的Python實現-Python程式設計原則、哲學和規範大彙總

全網最適合入門的物件導向程式設計教程:29 類和物件的Python實現-斷言與防禦性程式設計和help函式的使用

全網最適合入門的物件導向程式設計教程:30 Python的內建資料型別-object根類

全網最適合入門的物件導向程式設計教程:31 Python的內建資料型別-物件Object和型別Type

全網最適合入門的物件導向程式設計教程:32 Python的內建資料型別-類Class和例項Instance

全網最適合入門的物件導向程式設計教程:33 Python的內建資料型別-物件Object和型別Type的關係

全網最適合入門的物件導向程式設計教程:34 Python的內建資料型別-Python常用複合資料型別:元組和命名元組

全網最適合入門的物件導向程式設計教程:35 Python的內建資料型別-文件字串和__doc__屬性

全網最適合入門的物件導向程式設計教程:36 Python的內建資料型別-字典

全網最適合入門的物件導向程式設計教程:37 Python常用複合資料型別-列表和列表推導式

全網最適合入門的物件導向程式設計教程:38 Python常用複合資料型別-使用列表實現堆疊、佇列和雙端佇列

全網最適合入門的物件導向程式設計教程:39 Python常用複合資料型別-集合

全網最適合入門的物件導向程式設計教程:40 Python常用複合資料型別-列舉和enum模組的使用

全網最適合入門的物件導向程式設計教程:41 Python常用複合資料型別-佇列(FIFO、LIFO、優先順序佇列、雙端佇列和環形佇列)

全網最適合入門的物件導向程式設計教程:42 Python常用複合資料型別-collections容器資料型別

全網最適合入門的物件導向程式設計教程:43 Python常用複合資料型別-擴充套件內建資料型別

全網最適合入門的物件導向程式設計教程:44 Python內建函式與魔法方法-重寫內建型別的魔法方法

更多精彩內容可看:

給你的 Python 加加速:一文速通 Python 平行計算

一文搞懂 CM3 微控制器除錯原理

肝了半個月,嵌入式技術棧大彙總出爐

電子計算機類比賽的“武林秘籍”

一個MicroPython的開源專案集錦:awesome-micropython,包含各個方面的Micropython工具庫

Avnet ZUBoard 1CG開發板—深度學習新選擇

SenseCraft 部署模型到Grove Vision AI V2影像處理模組

文件和程式碼獲取:

可訪問如下連結進行對文件下載:

https://github.com/leezisheng/Doc

image

本文件主要介紹如何使用 Python 進行物件導向程式設計,需要讀者對 Python 語法和微控制器開發具有基本瞭解。相比其他講解 Python 物件導向程式設計的部落格或書籍而言,本文件更加詳細、側重於嵌入式上位機應用,以上位機和下位機的常見串列埠資料收發、資料處理、動態圖繪製等為應用例項,同時使用 Sourcetrail 程式碼軟體對程式碼進行視覺化閱讀便於讀者理解。

相關示例程式碼獲取連結如下:https://github.com/leezisheng/Python-OOP-Demo

正文

資料結構基本知識

實際上,前文講到的佇列、列表、集合、字典等均屬於資料結構的一部分。資料結構,作為一種程式碼結構,其核心目的在於有序地儲存與組織資料,進而簡化資訊的修改、導航與訪問過程。它不僅決定了資料的收集方式、實現的功能以及資料間的相互關係,更在電腦科學和程式設計的各個領域,如作業系統、前端開發以及機器學習等,發揮著不可或缺的作用。資料結構是解決問題的高效且真實的關鍵構件。每一種資料結構都有其獨特的適用場景和任務。

資料結構可劃分為邏輯結構與物理結構兩大類:

  1. 邏輯結構:是指資料元素間邏輯關係的資料組織方式,這種關係側重於元素間的相對位置,而與資料在計算機中的實際儲存位置無關。

在邏輯結構的分類中,可以分為線性結構和非線性結構:

  1. 物理結構:又稱儲存結構,描述的是資料邏輯結構在計算機儲存空間中的具體實現形式。資料的邏輯結構通常可以對映為多種物理結構。物理結構是實現資料元素間邏輯關係的具體手段。一種邏輯結構可以根據實際需求轉化為多種物理結構。常見的物理結構有:
    • ① 順序儲存,其特點是元素在記憶體中的儲存順序是連續的,線性表的元素透過一組連續的儲存單元來儲存。
    • ② 鏈式儲存,在這種方式中,記憶體中的儲存元素不必連續,每個元素節點包含資料元素和指向相鄰元素的指標資訊。
    • ③ 索引儲存,除了儲存節點資訊,還透過附加的索引表來標識節點的記憶體地址,索引表由多個索引項構成。
    • ④ 雜湊儲存,又稱 Hash 儲存,其中節點的儲存地址由節點的關鍵碼值直接決定。

接下來,我們將簡要介紹如何利用物件導向程式設計的思維和 Python 內建資料型別去實現其他的資料結構,注意該部分並非本書重點內容,所以這裡只是淺顯的講一下資料結構,其中實現程式碼主要是幫助讀者理解而非實際應用。

Python 實現連結串列

連結串列是一種物理儲存單元上非連續、非順序的儲存結構,是由許多相同資料型別的資料項按照特定順序排列而成的線性表。連結串列有一系列節點組成,所謂節點就是指連結串列中的每一個元素,每個節點包含兩個資料,一個是儲存元素的資料域(值),另一個是儲存下一個節點地址的指標域。

image

連結串列中的第一個節點稱為頭節點,最後一個節點稱為尾節點,其中尾節點的 next 指向為 null。連結串列可以是單鏈,也可以是雙鏈,這取決於每個節點是隻有一個指向下一個節點的指標,還是還有一個指向前一個節點的指標。你可以把連結串列想象成一條鏈;單個連結只與相鄰的連結有一個連線,但所有連結一起形成一個更大的結構。

class Node:
    def __init__(self, dataval=None):
        self.dataval = dataval
        self.nextval = None
class SLinkedList:
    def __init__(self):
        self.headval = None
list1 = SLinkedList()
list1.headval = Node("Mon")
e2 = Node("Tue")
e3 = Node("Wed")
_# Link first Node to second node_
list1.headval.nextval = e2
_# Link second Node to third node_
e2.nextval = e3

連結串列具有以下優勢:新元素插入和刪除更高效、比陣列更易於重組、高階資料結構 (如圖形或樹)都是基於連結串列的,缺點是:每個資料點的指標儲存增加了記憶體使用量同時必須始終從頭節點遍歷連結串列以查詢特定元素。

Python 實現樹

樹是另一種基於關係的資料結構,專門用於表示層次結構。與連結串列一樣,它們也被 Node 物件填充,Node 物件包含一個資料值和一個或多個指標,用於定義其與直接節點的關係。每棵樹都有一個根節點,所有其他節點都從根節點分支出來。根節點包含指向它正下方所有元素的指標,這些元素被稱為它的子節點。這些子節點可以有它們自己的子節點。在同一層上的任何節點都稱為同級節點。沒有連線子節點的節點稱為葉節點。

image

我們平時用到最多的就是二叉樹,二叉樹最常見的應用是二叉搜尋樹。二叉搜尋樹擅長於搜尋大量的資料集合,因為時間複雜度取決於樹的深度而不是節點的數量。二叉樹具有以下特點:

  • 左子樹只包含元素小於根的節點。
  • 右子樹只包含元素大於根的節點。
  • 左右子樹也必須是二叉搜尋樹。他們必須以樹的“根”來遵循上述規則。
  • 不能有重複的節點,即不能有兩個節點具有相同的值。
lass Node:
    def __init__(self, data):
        self.left = None
        self.right = None
        self.data = data
    def insert(self, data):
_# Compare the new value with the parent node_
        if self.data:
            if data < self.data:
                if self.left is None:
                    self.left = Node(data)
                else:
                    self.left.insert(data)
            elif data > self.data:
                if self.right is None:
                    self.right = Node(data)
                else:
                    self.right.insert(data)
        else:
            self.data = data
_# Print the tree_
    def PrintTree(self):
        if self.left:
            self.left.PrintTree()
        print( self.data),
        if self.right:
            self.right.PrintTree()
_# Use the insert method to add nodes_
root = Node(12)
root.insert(6)
root.insert(14)
root.insert(3)
root.PrintTree()

樹形結構的搜尋效率高非常適合儲存分層資料(如檔案位置等),但僅僅適用於排序的列表,未排序的資料退化為線性搜尋。

Python 實現雜湊表

雜湊表又叫雜湊表,雜湊表是一種複雜的資料結構,能夠儲存大量資訊並有效檢索特定元素。此結構儲存的是由鍵(key)和值(value)組成的資料,根據鍵直接訪問儲存在記憶體儲存位置的資料結構。

image

每個輸入鍵都要經過一個雜湊函式,該函式將其從初始形式轉換為一個整數值,稱為雜湊。雜湊函式必須始終從相同的輸入產生相同的雜湊,必須快速計算,併產生固定長度的值。Python 包含一個內建的 hash()函式,可以加速實現。然後,該表使用雜湊來查詢所需值(稱為儲存桶)的一般位置。然後,程式只需要在這個子組中搜尋所需的值,而不必搜尋整個資料池。除了這個通用框架之外,根據應用程式的不同,雜湊表也可能非常不同。有些可能允許來自不同資料型別的鍵,而有些可能有不同的設定桶或不同的雜湊函式。

下面是一個 Python 程式碼中的雜湊表示例:

import pprint
class Hashtable:
    def __init__(self, elements):
        self.bucket_size = len(elements)
        self.buckets = [[] for i in range(self.bucket_size)]
        self._assign_buckets(elements)
    def _assign_buckets(self, elements):
        for key, value in elements: _#calculates the hash of each key_
            hashed_value = hash(key)
            index = hashed_value % self.bucket_size _# positions the element in the bucket using hash_
            self.buckets[index].append((key, value)) _#adds a tuple in the bucket_
    def get_value(self, input_key):
        hashed_value = hash(input_key)
        index = hashed_value % self.bucket_size
        bucket = self.buckets[index]
        for key, value in bucket:
            if key == input_key:
                return(value)
        return None
    def __str__(self):
        return pprint.pformat(self.buckets) _# pformat returns a printable representation of the object_
if __name__ == "__main__":
     capitals = [
        ('France', 'Paris'),
        ('United States', 'Washington D.C.'),
        ('Italy', 'Rome'),
        ('Canada', 'Ottawa')
    ]
hashtable = Hashtable(capitals)
print(hashtable)
print(f"The capital of Italy is {hashtable.get_value('Italy')}"

雜湊表可以將任何形式的鍵隱藏為整數索引,對於大型資料集搜尋非常有效,常常應用於用於頻繁查詢的大型資料庫或根據關鍵字檢索的系統。

Python 實現圖

圖是一種資料結構,用於表示資料頂點(圖的節點)之間關係的視覺化。將頂點連線在一起的連結稱為邊。邊定義了哪些頂點被連線,但沒有指明它們之間的流向。每個頂點與其他頂點都有連線,這些連線以逗號分隔的列表形式儲存在頂點上。還有一種特殊的圖叫做有向圖,它定義了關係的方向,類似於連結串列。在建模單向關係或類似流程圖的結構時,有向圖很有幫助。

在 Python 中,圖的最佳實現方式是使用字典,每個頂點的名稱作為鍵,邊列表作為值。

_# Create the dictionary with graph elements_
graph = { "a" : ["b","c"],
                 "b" : ["a", "d"],
                 "c" : ["a", "d"],
                  "d" : ["e"],
                  "e" : ["d"]
         }
_# Print the graph          _
print(graph)

圖在儲存資料上有著比較複雜和高效的演算法,分別有鄰接矩陣 、鄰接表、十字連結串列、鄰接多重表、邊集陣列等儲存結構。常見的圖遍歷演算法就是廣度優先演算法和深度優先演算法。它適用於網路或類似網路的結構建模,可以透過程式碼快速傳達視覺資訊,但是在大型圖中很難理解頂點連結同時從圖表中解析資料的時間昂貴。

Python 實現堆

堆比較特殊,是一種圖的樹形結構。被用於實現“優先佇列”(priority queues),優先佇列是一種資料結構,可以自由新增資料,但取出資料時要從最小值開始按順 序取出。在堆的樹形結構中,各個頂點被稱為“結點”(node),資料就儲存在這些結點中。只要滿足下面兩個特點的樹形結構就是堆:

  • 堆是一個完全二叉樹(所謂完全二叉樹就是除了最後一層其他層的節點個數都是滿的)。
  • 堆中每一個節點的值都必須大於等於或者小於其子樹中每一個節點的值。

image

我們可以使用 heapq 模組實現一個適合與 Python 的列表一起使用的最小堆:

import heapq
from heapq_showtree import show_tree
from heapq_heapdata import data

heap = []
print('random :', data)
print()

for n in data:
    print('add {:>3}:'.format(n))
    heapq.heappush(heap, n)
    show_tree(heap)

裡面有兩個函式需要注意:

  • heapq.heappush(heap, item):將值 item 推送到 heap,保持堆不變。
  • heapq_showtree.show_tree(heap):用於展示樹形結構。

image

相關文章