基本線性資料結構的Python實現
本篇主要實現四種資料結構,分別是陣列、堆疊、佇列、連結串列。我不知道我為什麼要用Python來幹C乾的事情,總之Python就是可以幹。
所有概念性內容可以在參考資料中找到出處
陣列
陣列的設計
陣列設計之初是在形式上依賴記憶體分配而成的,所以必須在使用前預先請求空間。這使得陣列有以下特性:
- 請求空間以後大小固定,不能再改變(資料溢位問題);
- 在記憶體中有空間連續性的表現,中間不會存在其他程式需要呼叫的資料,為此陣列的專用記憶體空間;
- 在舊式程式語言中(如有中階語言之稱的C),程式不會對陣列的操作做下界判斷,也就有潛在的越界操作的風險(比如會把資料寫在執行中程式需要呼叫的核心部分的記憶體上)。
因為簡單陣列強烈倚賴電腦硬體之記憶體,所以不適用於現代的程式設計。欲使用可變大小、硬體無關性的資料型別,Java等程式設計語言均提供了更高階的資料結構:ArrayList、Vector等動態陣列。
Python的陣列
從嚴格意義上來說:Python裡沒有嚴格意義上的陣列。
List
可以說是Python裡的陣列,下面這段程式碼是CPython的實現List
的結構體:
123456789101112131415161718 | typedefstruct{PyObject_VAR_HEAD/*Vector of pointers to listelements.list[0]isob_item[0],etc.*/PyObject**ob_item;/*ob_item contains space for'allocated'elements.The number*currently inuse isob_size.*Invariants:*0<=ob_size<=allocated |
還有一篇文章講List
實現,感興趣的朋友可以去看看。中文版。
當然,在Python裡它就是陣列。
後面的一些結構也將用List
來實現。
堆疊
什麼是堆疊
堆疊(英語:stack),也可直接稱棧,在電腦科學中,是一種特殊的串列形式的資料結構,它的特殊之處在於只能允許在連結串列或陣列的一端(稱為堆疊頂端指標,英語:top)進行加入資料(英語:push)和輸出資料(英語:pop)的運算。另外堆疊也可以用一維陣列或連結串列的形式來完成。堆疊的另外一個相對的操作方式稱為佇列。
由於堆疊資料結構只允許在一端進行操作,因而按照後進先出(LIFO, Last In First Out)的原理運作。
特點
- 先入後出,後入先出。
- 除頭尾節點之外,每個元素有一個前驅,一個後繼。
操作
從原理可知,對堆疊(棧)可以進行的操作有:
- top():獲取堆疊頂端物件
- push():向棧裡新增一個物件
- pop():從棧裡推出一個物件
實現
123456789101112131415161718192021222324252627282930313233343536373839 | classmy_stack(object):def __init__(self,value):self.value=value# 前驅self.before=None# 後繼self.behind=Nonedef __str__(self):returnstr(self.value)def top(stack):ifisinstance(stack,my_stack):ifstack.behind isnotNone:returntop(stack.behind)else:returnstackdef push(stack,ele):push_ele=my_stack(ele)ifisinstance(stack,my_stack):stack_top=top(stack)push_ele.before=stack_toppush_ele.before.behind=push_eleelse:raise Exception('不要亂扔東西進來好麼')def pop(stack):ifisinstance(stack,my_stack):stack_top=top(stack)ifstack_top.before isnotNone:stack_top.before.behind=Nonestack_top.behind=Nonereturnstack_topelse:print('已經是棧頂了') |
佇列
什麼是佇列
和堆疊類似,唯一的區別是佇列只能在隊頭進行出隊操作,所以佇列是是先進先出(FIFO, First-In-First-Out)的線性表
特點
- 先入先出,後入後出
- 除尾節點外,每個節點有一個後繼
- (可選)除頭節點外,每個節點有一個前驅
操作
- push():入隊
- pop():出隊
實現
普通佇列
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 | classMyQueue():def __init__(self,value=None):self.value=value# 前驅# self.before = None# 後繼self.behind=Nonedef __str__(self):ifself.value isnotNone:returnstr(self.value)else:return'None'def create_queue():"""僅有隊頭"""returnMyQueue()def last(queue):ifisinstance(queue,MyQueue):ifqueue.behind isnotNone:returnlast(queue.behind)else:returnqueuedef push(queue,ele):ifisinstance(queue,MyQueue):last_queue=last(queue)new_queue=MyQueue(ele)last_queue.behind=new_queuedef pop(queue):ifqueue.behind isnotNone:get_queue=queue.behindqueue.behind=queue.behind.behindreturnget_queueelse:print('佇列裡已經沒有元素了')def print_queue(queue):print(queue)ifqueue.behind isnotNone:print_queue(queue.behind) |
連結串列
什麼是連結串列
連結串列(Linked list)是一種常見的基礎資料結構,是一種線性表,但是並不會按線性的順序儲存資料,而是在每一個節點裡存到下一個節點的指標(Pointer)。由於不必須按順序儲存,連結串列在插入的時候可以達到O(1)的複雜度,比另一種線性表順序錶快得多,但是查詢一個節點或者訪問特定編號的節點則需要O(n)的時間,而順序表相應的時間複雜度分別是O(logn)和O(1)。
特點
使用連結串列結構可以克服陣列連結串列需要預先知道資料大小的缺點,連結串列結構可以充分利用計算機記憶體空間,實現靈活的記憶體動態管理。但是連結串列失去了陣列隨機讀取的優點,同時連結串列由於增加了結點的指標域,空間開銷比較大。
操作
- init():初始化
- insert(): 插入
- trave(): 遍歷
- delete(): 刪除
- find(): 查詢
實現
此處僅實現雙向列表
Python12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 | classLinkedList():def__init__(self,value=None):self.value=value# 前驅self.before=None# 後繼self.behind=Nonedef__str__(self):ifself.value isnotNone:returnstr(self.value)else:return'None'definit():returnLinkedList('HEAD')defdelete(linked_list):ifisinstance(linked_list,LinkedList):iflinked_list.behind isnotNone:delete(linked_list.behind)linked_list.behind=Nonelinked_list.before=Nonelinked_list.value=Nonedefinsert(linked_list,index,node):node=LinkedList(node)ifisinstance(linked_list,LinkedList):i=0whilelinked_list.behind isnotNone:ifi==index:breaki+=1linked_list=linked_list.behindiflinked_list.behind isnotNone:node.behind=linked_list.behindlinked_list.behind.before=nodenode.before,linked_list.behind=linked_list,nodedefremove(linked_list,index):ifisinstance(linked_list,LinkedList):i=0whilelinked_list.behind isnotNone:ifi==index:breaki+=1linked_list=linked_list.behindiflinked_list.behind isnotNone:linked_list.behind.before= |