《算法導論》學習之旅-第十章-基本數據結構


序言

在本章中,我們將會討論如何使用指針的基本數據結構來構造動態集合,下面主要介紹:隊列鏈表有根樹,此外還要介紹由數組構造對象和指針的方法。

棧和隊列

棧和隊列都是動態集合,對他們的刪除操作都是設定好的。在中的刪除操作採用的是先進後出策略;而在隊列當中則是使用的是先進先出策略。下面將介紹如何使用簡單的數組來實現這兩種數據結構。

棧的INSERT操作通常被稱爲壓入(PUSH),而刪除操作則是成爲彈出(POP)。這種數據結構就好比餐廳中的疊起來的盤中,我們每次都是上面放入,然後拿走的時候也是從最頂上的一個盤子拿走。

我們可以用一個數組來表示他,如,A[1…n]表示一個容納最多有n個元素的棧,但有個棧頂屬性A.top,它指向的是最新元素的位置,則棧包含的元素就是A[1…A.top],其中A[1]表示棧底元素。
在這裏插入圖片描述
當A.top = 0的時候,這個棧就是空棧,通常我們會採用判空的函數進行判斷(STACK-EMPTY),下面是判空,壓入,和彈出操作的僞代碼:

STACK-EMPTY(S)       //這是判空操作
if S.top == 0
	return TRUE
else return FALSE

PUSH(S, x)          //壓入操作
S.top = S.top+1
S[S.top] = x

POP(S)				//彈出操作
if STACK-EMPTY(S)
	error "underflow"
else  S.top = S.top - 1
	return S.top[S.top+1]

上述的僞代碼是比較簡單的,如何壓入操作的時候是沒有考慮上溢出的情況。

隊列

隊列的INSERT操作稱爲入隊,而DELITE則稱之爲出隊操作。隊列這種數據結構就像是食堂打飯時排隊一樣,遵循的是先排先出的原則。

利用Q[1…N]表示出最多可以容納n-1個元素的隊列.隊列有一個屬性:Q.tailQ.headQ.tail 指向下一個新元素要插入的位置,Q.head 指向隊頭元素。當Q.head = Q.tail時隊列爲空,當Q.head = Q.tail+1表示隊滿。
在這裏插入圖片描述

下面的入隊和出隊操作的僞代碼(省略了對上溢和下溢的檢測):

ENQUEUE(Q, x)				//入隊操作
Q[Q.tail] = x
if Q.tail == Q.length
	Q.tail = 1
else Q.tail = Q.tail + 1

DEQUEUE(Q)					//出隊操作
x = Q.head
if Q.head = Q.length
	Q.head = 1
else Q.head = Q.head+1
return 1

這兩種操作的時間都是O(1)

鏈表

鏈表(linked list)是一種這樣的數據結構,其中的各對象按線性順序排列。數組的線性順序是由數組下標決定的,然而與數組不同的是,鏈表的順序是由各個對象裏的指針決定的。鏈表爲動態集合提供了一種簡單而靈活的表示方法。

鏈表有單鏈表雙鏈表循環鏈表。書中着重介紹了雙鏈表的概念及操作,雙鏈表L的每一個元素是一個對象,每個對象包含一個關鍵字:key和兩個指針:next和prev。鏈表的操作包括插入一個節點(insert)、刪除一個節點(delete)和查找一個節點(search),如果雙鏈表的對象L.prev = NIL 則表示鏈表的頭,L.next = NIL 則表示鏈表的尾。下面是雙鏈表的示意圖:
在這裏插入圖片描述
而單鏈表時省略掉了L.prev指針。循環鏈表的表頭指針L.prev指向的是表尾元素的位置,我們可以將一個循環鏈表看成是一個圓環。

下面介紹幾種鏈表的操作:

鏈表的搜索

鏈表的搜索操作是採用的簡單線性搜索方法,主要用於查找鏈表L中的第一個關鍵字K的元素,如果鏈表中沒有該對象,則返回NIL。搜索操作的僞代碼爲:

LIST_SEARCH(L, k)
x = L.head
while x != NIL and x.key != k:
	x = x.next
return x

要搜索一個有n個對象的鏈表時,最壞情況的運行時間爲O(n)

鏈表的插入

插入操作是將要插入的x結點連接在鏈表的前端,如僞代碼所示:

LIST-INSERT(L, x)
x.next = L.head
if L.head != NIL 
	L.head.prev = x
L.head = x
x.prev = NIL

一個含有n個元素的鏈表執行的運行時間是O(1)

鏈表的刪除

刪除過程就是將一個元素x從鏈表L中移除,如:

LIST-DELETE(L, x)
if x.prev != NIL
	x.prev.next = x.next
else L.head = x.next
if x.next != NIL
	x.next.prev = x.prev

刪除操作的時間,分爲兩種情況:1.刪除一個元素,則他的時間是O(1)
2. 如何是刪除某一個關鍵字爲k的元素,則最壞的情況下可能用到的時間爲O(n)

指針和對象的實現

有些語言不支持指針和對象數據類型時,我們需要實現鏈式數據結構的兩種方法,下面我們將介紹數組和數組下標來構造對象和指針。

對象的多數組表示

對每個屬性都用一個數組表示。如下圖所示:
在這裏插入圖片描述
prev和next數組中存放的數字就是他們所指向節點的位置,此外變量L存放的是表頭元素的下標

對象的單數組表示

計算機中的內存往往是從整數0到M-1進行編址。用一個數組來表示一個鏈表,對於雙鏈表就是連續的三個單元表示一個結點。如下圖所示:
在這裏插入圖片描述

有根樹的表示

這一小節將討論用鏈式儲存結構表示有根樹的問題,首先我們先討論二叉樹,然後給出針對節點的孩子數任意的有根樹的表示方法。

二叉樹

下面是二叉樹的示意圖:
在這裏插入圖片描述
二叉樹的每一個節點都含有父節點p,左孩子left,右孩子right的指針。如果x.p = NIL,則x爲根節點。如果x.left和x.right都是NIL,則則表示x爲葉結點。屬性T.root指向整棵樹T的根結點,如果T.root = NIL, 則樹爲空。

分支無限制的有根樹

二叉樹的表示方法可以推廣到每個結點的孩子數都至多爲常數k的任意類型的樹:只需要將left和right屬性用child1 , child2 ,…,childk來表示。但是由於孩子數的不定性,我們就不知道改爲其分配多少個數組。此外,及時孩子數k限制在一個大的常數以內,若多數的節點都只有少量的孩子,則會浪費大量的空間。

下面就有一種叫做左孩子右兄弟表示法可以解決該類問題。這種方法和前面的類似,每個結點都包含一個父節點指針p,且T.root指向樹T的根結點
。然而每個結點就只包含兩個指針:

  1. x.left-child指向的結點x最左邊的孩子結點
  2. x.right-sibling指向的是x右側相鄰的兄弟結點

如果結點x沒有孩子結點, 則x.left-child = NIL;如果結點x是其父節點的最右孩子,則x.right-sibling = NIL。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章