算法导论之基本数据结构
1.栈
栈是一种很常用的,基本的数据结构,实现的是后进先出的策略。
栈的几种操作可以通过以下的伪代码来实现:
(1)判断栈是否为空
STACK-EMPTY(S):
if S.top==0
return TRUE;
Else return FALSE;
(2)压栈
PUSH(S,x):
S.top=S.top+1;
S[S.top]=x;
(3)出栈
POP(S):
if STACK-EMPTY(S)
Error “underflow”;
else
S.top=S.top-1;
Return S[S.top+1];
2.队列
队列和栈一样,都属于比较基本的数据结构,与栈相比较,很明显的区别就是,队列是一种先进先出的策略
(1)进队列
ENQUEUE(Q,x):
Q[Q.tail]=x;
if Q.tail==Q.length
Q.tail=1;
else Q.tail=Q.tail+1;
(2)出队列
DEQUEUE(Q):
x=Q[Q.head];
If Q.head=Q.length;
Q.head=1;
else Q.head=Q.head+1;
return x;
3.链表
链表是一种这样的数据结构,其中的个对象按线性顺序进行排序。数组的线性顺序是由数组下标决定的,然而与数组不同的是,链表的顺序是由各个对象里的指针决定的。
双向链表:
(1)链表的搜索
LIST-SEARCH(L,k):
x=L.head;
while x!=NULL andx.key!=k
x=x.next;
return x;
(2)链表的插入
LIST-INSERT(L,x):
x.next=L.head;
if L.head!=NULL
L.head.prev=x;
L.head=x;
X.prev=NULL;
(3)链表的删除
LIST-DELETE(L,x)
if x.prev!=NULL;
x.prev.next=x.next;
else L.head=x.next;
if x.next!=NULL;
x.next.prev=x.prev;
我们可以将哨兵加进去,将哨兵加在表头和表尾之间,这样就可以将双向链表改造成一个双向循环链表。
双向循环链表(如下图所示):
LIST-SEARCH的代码和之前基本保持不变,只是对NULL和L.head的引用如前所述加以调整:
(1)链表的搜索
LIST-SEARCH’(L,k):
x=L.nil.next;
while x!=L.nil andx.key!=k
x=x.next;
return x;
(2)链表的插入
LIST-INSERT’(L,x):
x.next=L.nil.next;
L.nil.next.prev=x;
L.nil.next=x;
x.prev=L.nil;
(3)链表的删除
LIST-DELETE’(L,x):
x.prev.next=x.next;
X.next.prev=x.prev;
4.有限树
现在讨论的是树的这种数据结构中,当孩子结点的数目没有限制的时候,如何进行表示。
我们可用左孩子有兄弟的方式来进行表示。该方法的优势在于,对任意n个结点的有根树,只需要O(n)的存储空间。每个结点都包含一个父结点指针p,且T.root指向树T的根节点。然而,每个结点中不是包含指向每个孩子的指针,而是只有两个指针:
1.x.left-child指向结点x最左边的孩子结点
2.X.right-sibling指向x右邻的兄弟结点
如果结点x没有孩子结点,则x.left-child=NULL,如果结点x是其父节点的最右孩子,则x.right-sibling=NULL。
如下图示:
下面所示是一般的二叉树的结构示意图: