栈和队列都是线性表:
栈和队列都是特殊的线性表,只不过对插入和删除操作做了限制。
栈:限定仅在表尾进行插入和删除操作的线性表。
队列:只允许在一端进行插入操作,而在另一端进行删除操作的线性表
栈和队列的顺序存储结构:
它们都可以用线性表的顺序存储结构实现,但都存在着顺序存储的一些弊端,因此它们各自有一些技巧解决这个问题。
对于栈来说,如果两个相同数据类型的栈,则可以用数组的两端作栈底的方法让两个栈共享数据,最大化利用数组的空间。
对于队列来说,为了避免数组插入和删除时需要移动数据,引入循环队列,使得本来插入和删除是O(n)的时间复杂度变成了O(1)。
1. 栈(stack)
栈是一种只能在同一端进行插入或删除操作的线性表。
按照先进后出(FILO)的原则存储数据
基本运算:
push() 进栈,向栈顶添加元素
pop() 出栈,删除栈顶元素
empty() 判断栈是否为空
gettop() 取栈顶操作,返回当前的栈顶元素
顺序栈实现的常规做法:
# 顺序栈类示例
class SqStack:
def __init__(self):
self.data = []
def empty(self): # 判断栈是否为空
if len(self.data) == 0:
return True
return False
def push(self, e) # 元素e进栈
self.data.append(e)
def pop(self, e) # 元素e出栈
assert not self.empty()
return self.data.pop()
def gettop(self): # 取栈顶元素
assert not self.empty()
return self.data[-1]
链栈可以用带头结点的单链表实现。
# 链栈示例
class LinckNode:
def __init__(self, data = None):
self.data = data
self.next = None
class LinkStack:
def __init__(self):
self.head = LinkNode()
self.head.next = None
def empty(self):
if self.head.next == None:
return True
return False
def push(self, e): # 将元素e的节点插入到self.head节点后
p = LinkNode(e)
p.next = self.head.next
self.head.next = p
def pop(self):
assert self.head.next != None
p = self.head.next
self.head.next = p.next
return p.data
def gettop(self):
assert self.head.next != None
return self.head.next.data
2. 队列(queue)
队列是一种只能在不同端进行插入或删除操作的线性表。
插入端称作队尾rear,删除端称作队首front。
按照先入先出原则(FIFO)
主要作用:解耦,是程序实现松耦合(一个模块修改不会影响其他模块)
队列中数据只有一份,取出就没有了。区别于列表,列表数据取出只是复制了一份
基本运算:
empty() 判断队列是否为空,若队列为空,返回True;否则返回False
push(e) 进队,将元素e进队作为队尾元素
pop() 出队,从队头出队一个元素
gethead() 取队头,返回队头元素而不出队
顺序队列:
(1)非循环队列
用data列表来存放队列中元素,约定队头指针为front(实际上是队头元素的前一个位置),队尾指针为rear(正好是队尾元素的位置)。
为了简单,data列表使用固定容量MaxSize。
初始时设置front和rear均为-1(front==rear)
队空条件:front == rear
队满条件:rear == MaxSize - 1
元素x进队:rear++,将元素x放置在该位置
元素出队:front++,取出该位置的元素
(2)循环队列
为解决假溢出,引入循环队列。
(假溢出:在非循环队列中不断进队和出队会导致front和rear越来越大,当rear == MaxSize - 1时判断为队满,但其实在front前可能还有很多空位,所以是假溢出。)
初始时设置front和rear均为0
判断队列满条件不能与判断队列空相同,所以少用一个元素空间来判断。(也就是说data数组的length=n,但MaxSize=n-1)
队空条件:rear==front
队满条件:(rear+1) % MaxSize==front(相当于试探进队一次,若rear达到front,则认为队满了)
元素e进队:rear=(rear+1)%MaxSize,将元素e放置在该位置
元素出队:front=(front+1)%MaxSize,取出该位置的元素
用循环队列data[0,…,m]存放其元素值,用front和rear分别表示队头和队尾,则当前队列重的元素数是:(rear-front+m) % m
(3)链队
不带头指针,self.front就是第一个结点
队空条件:front==rear==None,可以以front==None作为队空条件
队满条件:内存溢出时才出现,通常不考虑
元素e进队:在单链表尾部插入存放e的s结点,并让队尾指针指向它
出队:取出队首结点的data值,并将其从链队中删除