算法导论第十章:基本数据结构
引言 数据结构
集合:如同在数学中一样,集合也是计算机科学的基础。不过数学上的集合时不变的,而算法所操作的集合是动态改变的。数据结构这一部分介绍在计算机中表示和操作有穷动态集合的一些基本技术。
字典:许多算法要求能够将元素插入集合,从集合中删除元素,以及测试元素是否属于集合。支持这些操作的动态集合就叫字典。另一些算法可能需要更复杂的操作,实现动态集合的最好方案取决于要支持什么样的集合操作。
动态集合上的操作:下面给出一些典型的操作
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。
第十章:基本数据结构
10.1栈和队列
栈和队列都是动态集合,可以用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]<head[Q]+1
5 return x
10.2链表
双链表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就永远不会为空。
10.4有根数的表示
链表的表示方法可以推广至任意同构的数据结构上。这一节讨论用链节数据结构表示有根树的问题。
二叉树
用域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紧右边的兄弟。(左孩子,右兄弟)