数据结构之栈、队列、链表

520 阅读3分钟

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

基本数据结构

引言:本文本篇主要介绍的是几种比较基础并常见的数据结构:链表、栈以及队列。

栈实现的是一种先入后出的策略,也就是先插入的元素最后被删除。在计算机中栈的实现方式有很多,这里我们简单用数组来实现。其结构类似如下:

image.png

其中先插入的元素放在栈底,后插入的放在栈顶。当要取元素的时候则取出栈顶的元素。可以用一个数组来表示栈,其中数组有一个top属性,用来指向最新插入的元素。

image.png

在上图中,该数组就是一个栈,其中S.top指向栈顶,当要取出元素时候,先返回S.top指向的元素,然后再S.top--,而取出的元素还在数组中,但是其已经不在栈里面了。

关于数组栈的操作的伪代码如下:

//判断栈是否为空
STACK-EMPTY(S) // 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
    return S[S.top--]

队列

队列和栈类似都是动态集合,不过队列实现的是一种先入先出的策略。队列的实现方式很多,这边还是采用数组来实现。队列的结构如下:

image.png

用数组来实现队列的话,需要两个指针,一个指向队尾一个指向队头,分别设为Q.tail,Q.head,当插入元素时Q.tail+1,当取出元素时Q.head+1当队列为空的时候则Q.tail==Q.head,不过当队空的时候删除会导致下溢,队满的时候插入会导致上溢。所以需要判断是否溢出。

其队列的伪代码如下:

// 判断是否为空
QUEUE-EMPTY(Q)
if Q.tail==Q.head
    return TRUE
return FALSE

QUEUE-FULL(Q)
if (Q.tail+1)%Q.size==Q.head
    return TRUE
return FALSE

ENQUEUE(Q,x)
if QUEUE-FULL(Q)
    error 'Overflow'
Q[Q.tail] = x
if Q.tail==Q.length
    Q.tail = 1
else  Q.tail = Q.tail+1

DEQUEUE(Q)
if QUEUE-EMPTY(Q)
    error 'underflow'
x = Q[Q.head]
if Q.head = Q.length
    Q.head = 1
else Q.head = Q.head+1
return x

链表

链表,别名链式存储结构或单链表,用于存储逻辑关系为 "一对一" 的数据。链表不限制数据的物理存储状态,即链表的顺序是随机的,其顺序由各个对象里面的指针决定。

链表主要由一个keynext以及一些卫星数据组成。当然在双向链表中,是由keynext以及prev组成,其中nextprev分别是指向结点前一结点和后一结点的两个指针。而key则是保存的数据.如果x.prev==NIL则表示该结点为链表的头,x.next==NIL则表示该结点为链表的尾。L.head指向链表的头结点,其如果等于NIL则表示链表为空.双向链表结构类似下图:

image.png

接下来介绍下,双向链表的插入删除和搜索:

搜索

链表的搜索采用线性搜索方式,查找某个关键字,然后返回该元素的指针。如果没有则返回NIL

LIST-SEARCH(L,k)
x = L.head
while x != NIL and x.key != k
    x = x.next
return x

插入

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

删除

删除操作是先找到指定的指针,然后再通过修改一些指针,将x删除出链表。

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