演算法導論第十章 基本資料結構

2021-05-23 17:41:36 字數 2854 閱讀 9927

集合:如同在數學中一樣,集合也是電腦科學的基礎。不過數學上的集合時不變的,而演算法所操作的集合是動態改變的。資料結構這一部分介紹在計算機中表示和操作有窮動態集合的一些基本技術。

字典:許多演算法要求能夠將元素插入集合,從集合中刪除元素,以及測試元素是否屬於集合。支援這些操作的動態集合就叫字典。另一些演算法可能需要更複雜的操作,實現動態集合的最好方案取決於要支援什麼樣的集合操作。

動態集合上的操作:下面給出一些典型的操作

search(s,k) : 在集合s中查詢關鍵字值為k的元素x;或者當不存在這樣的元素時返回nil。

insert(s,x):將元素新增到s中去。

delete(s,x):將x從s中刪除,注意引數是元素x的指標而不是關鍵字k。

minum(s):查詢返回s中具有最小關鍵字的元素。

maximum(s):查詢返回s中具有最大關鍵字的元素。

successor(s,x):查詢,s中元素x的後繼元素,當不存在時返回nil。

predecessor(s,x):查詢s中元素x的前繼元素,當不存在時返回nil。

棧和佇列都是動態集合,可以用delete操作去掉的元素時預先規定好的。在棧中,可以去掉元素是最近插入的哪乙個:棧實現了一種後進先出的策略;在佇列中,可以去掉的元素總是在集合中存在時間最長的那乙個:佇列實現了先進先出的策略。

棧上的insert操作稱為壓棧(push),無引數的delete操作稱為彈出(pop)。可以用陣列s[1...n]來實現乙個至多n個元素的棧,屬性top[s],指向最近插入的元素。s[1]是棧底元素,s[top[s]]是棧頂元素。當top[s]=0時,棧中不包含任何元素。

stack-empty(s)

1  if top[s]=0

2     then return true

3     else  return false

push(s, x)

1  top[s] <-- top[s]+1

2  s[top[s]] <-- x

pop(s)

1  if stack-empty(s)

2      then error "underflow"

3      else  top[s]<--top[s]-1

4               return s[top[s]+1]

佇列

佇列上插入操作稱為入佇列enqueue,刪除操作稱出佇列dequeue。可以用陣列q[1...n]來實現佇列,屬性head[q]指向佇列的頭,屬性tail[q],它指向新元素將會插入的地方。當head[q]=tail[q]時,隊列為空。當head[q]=tail[q]+1佇列滿。

enqueue(q,x)

1  q[tail[q]]<--x

2  if tail[q]=length[q]

3     then tail[q]<--1

4     else tail[q]<--tail[q]+1

dequeue(q)

1   x<--q[head[q]]

2   if head[q]=length[q]

3      then head[q]<--1

4      else  head[q]5   return x

雙鏈表l的每個元素都是物件,每個物件包含乙個關鍵字域和兩個指標域:next和prev。next[x]指向鍊錶中x的後繼元素,而prev[x]則指向x的前驅元素。如果prev[x]=nil則x是鍊錶的第乙個元素,如果next[x]=nil,則元素x是最後乙個元素。屬性head[l]指向鍊錶的第乙個元素。

list-search(l,k)

1  x <-- head[l]

2 while x!=nil and key[x]!=k

3    do x = next[x]

4  return x

list-insert(l,x)

1  next[x] = head[l]

2  if head[l]!=nil

3     then prev[head[l]]<--x

4  head[l] = x

5  prev[x]<--nil

list-delete(l,x)

1  if prev[x] != nil

2     then next[prev[x]] <-- next[x]

3     else head[l] <-- next[x]

4  if  next[x]!=nil

5     then prev[next[x]]<--prev[x]

哨兵:可以通過引入哨兵元素來簡化上面的**,哨兵就是個啞元物件。假設l頭部永遠有個nil[l]元素,l就永遠不會為空。

鍊錶的表示方法可以推廣至任意同構的資料結構上。這一節討論用鏈節資料結構表示有根樹的問題。

二叉樹

用域p、left和right來存放指向二叉樹t中的父親、左兒子和右兒子的指標。如果p[x]=nil,則x為根。如果left[x]=nil,x無左兒子,右兒子也類似。整個樹t的根由屬性root[t]指向,如果root[t]=nil,則樹為空。

分支數無限制的有根樹

二叉樹的方法可以推廣至每個結點的子女數至多為常數k任意種類的樹:用child1,child2...,childk來取代left和right域。如果結點的子女數是沒有限制的,這種方法就不合適了。

可以用二叉樹很方便地表示具有任意子女數的樹。每個結點有的left和right域分別替換成left-child和right-sibling,前者指向x的最左孩子,後者指向結點x緊右邊的兄弟。(左孩子,右兄弟)

演算法導論第十章 基本資料結構(一)

棧和佇列是動態集合 棧實現的是一種後進先出 lifo,last in first out 佇列實現的是一種先進先出 fifo 棧 stack 棧的操作有兩個,乙個是壓入 push 乙個是彈出 pop 對空棧執行pop操作,會導致乙個錯誤 棧下溢 underflow 如果棧頂元素超出棧的大小,那麼則導...

第十章 基本資料結構 鍊錶

鍊錶 鍊錶與陣列的區別是鍊錶中的元素順序是有各物件中的指標決定的,相鄰元素之間在物理記憶體上不一定相鄰。採用鍊錶可以靈活地表示動態集合。鍊錶有單鏈表和雙鏈表及迴圈鍊錶。書中著重介紹了雙鏈表的概念及操作,雙鏈表l的每乙個元素是乙個物件,每個物件包含乙個關鍵字和兩個指標 next和prev。鍊錶的操作包...

演算法導論第十章資料結構 雙向鍊錶

看的概念挺朦朧的,沒有明確好雙鏈表到底需要哪些方法,其實針對這種結構應該可以寫很多方法,並沒有什麼特定標準。不過真是只看不練不行啊,一下手各種錯誤,各種溢位 include using namespace std templatestruct node templateclass flist tem...