栈式限制在一端进行插入和删除操作的线性表,具有先进后出的特性,如图所示:
顺序栈在初始化时要定义栈的大小。设定 max 为栈的存储元素个数
代码如下:
class SeqStack(object): def __init__(self, max): # 顺序栈的最大容量 self.max =max # 当栈为空时, 栈定指针指向 -1 self.top = -1 # 存储栈元素的数组 self.stack = [None for i in range(self.max)]
如果 self.top 的数值为 -1 ,则表示空战, 返回 True,否则返回 False,代码如下:
def empty(self): return self.top is -1
代码如下:
def push(self, val): if self.top == self.max - 1: raise IndexError('栈已满') else: # 将栈顶指针加一 self.top += 1 self.stack[self.top] = val
代码如下:
def pop(self): if self.empty(): raise IndexError('栈空') else: # 此时top指向栈顶元素,存下栈顶元素的值,再将指针下移一个位置 cur = self.stack[self.top] sef.top -= 1 return cur
代码如下:
def peek(self): if self.empty(): raise IndexError('栈空!') else: return self.stack[self.top]
链栈为栈的链式存储结构,是运算受限的单链表,其插入和删除操作只能在表头位置进行。设链栈头指针为top,初始化top=None。
链栈节点结构与单链表节点结构相同,如图所示:
class Node(object): def __init__(self, val): # 节点的数据域 self.val = val # 节点的指针域 self.next = None
class LinkedStack(object): def __init__(self): self.top = None
def empty(self): return self.top is None
将待插入节点的 next 指针指向栈顶指针所指向的节点,将栈顶指针指向新节点,代码如下:
def push(self, val): """ :param val: 入栈元素 """ newNode = Node(val) # 新节点的直接后继指向栈顶指针 newNode.next = self.top # 将栈顶指针指向新节点 self.top = newNode
代码如下:
def pop(self): """ :return: 返回栈顶元素 """ # 如果栈为空,则抛出异常 if self.empty(): raise IndexError('栈为空') else: # temp用来存储栈顶元素 temp = self.top # 指针指向栈顶的下一个元素 setf.top = self.top.next return temp
def peek(self): """ :return : 返回栈顶元素 """ if self.empty(): raise IndexError('栈为空') else: return self.top.val
队列只允许在一端进行插入,在另一端进行删除,具有先进先出的特性。、
在顺序队列中,将元素入队时,只需要在队尾添加一个元素即可。但是,如果将元素出队,则除了将元素从队首删除之外,还需要将其他元素向前移动一个位置。
队列初始化时,规定了队列的最大元素容量为6。\(self.front、self.rear\)均指向下标为0的位置,如图所示:
##### 2. 顺序队列初始化class SeqQueue(object): def __init__(self, max): self.max = max self.front = 0 self.rear = 0 self.data = [None for i in range(self.max)]
现在向队列中添加3个元素,分别是5、3、7,添加元素后的队列如图所示:
每次插入都是在队尾插入的,也就是rear所在的位置。每次插入完成后rear自增1。现将队列中的所有元素出队,如图所示:
将队列中所有元素出队后,队列的起始位置已经由原本下标为0的位置移动到下标为3的位置了。那么,如果继续按这样入队、出队的形式,是不是到最后会存在这样一种现象—队列中是空的,没有任何元素,却无法继续将元素进队?结果是显而易见的,最后就是会出现这种现象。这种现象叫作假溢出。
为了解决假溢出现象,引入了循环队列的概念。所谓循环队列,就是将队列首尾连接起来。当rear的数值超过数组的最大长度时,先判断front是否在下标为0的位置,如果不在,则将rear指向下标0。
为方便理解,将循环队列表示为一个环形的图,起始时, rear 和 front 指向下标 0 ,如图所示:
现在向队列中添加5个元素,分别是5、3、7、8、4。在循环队列中,添加元素也是在队尾添加的,即rear下标处,如图所示:
从循环队列中删除两个元素,删除元素是在队首删除的,如图所示:
此时,在顺序队列中,当front等于rear时表示队空。但是在循环队列中,很难区分front等于rear时是空队还是满队。这里的解决方法是保留一个元素空间,当队列满时,还有一个空的位置。由于rear有可能比front大,也有可能比front小,所以尽管相差一个位置,其实有可能是相差一整圈。
若队列的最大容量为max,则队满的条件是(rear+1)%max==front
self.front 指向队首下标,self.rear 指向队尾下标,max 表示队列的最大容量。循环队列初始化代码实现如下:
class CircleQueue(object): def __init__(self, max): # 队列最大容量 self.max = max # 存储队列元素的数组 self.data = [None for i in range(self.max)] # 队首指针 self.front = 0 # 队尾指针 self.rear = 0
def empty(self): return self.front == self.rear
def push(self, val): """ :param val:待插入的关键字 """ if (self.rear + 1) % self.max == self.front: raise IndexError('队满') # 在队尾插入新的关键字 self.data[self.rear] = val # 修改队尾指针 self.rerr = (self.rear + 1) % self.max
代码如下:
def pop(self): if self.empty(): raise IndexError('队空') # 队列不为空,获取队首元素 cur = self.data[self.front] # 修改队首指针,指向下一个位置 self.front = (self.front + 1) % self.max # 返回原队首元素 return cur
代码如下:
def peek(self): if self.empty(): raise IndexError('队空') return self.data[self.front]
顺序队列在插入和删除时需要移动大量的元素,因此引入了链式队列。链式队列插入和删除操作方便,不需要移动元素,只需要改变指针的指向即可。
链式队列节点结构与单链表的节点结构一样,队列节点包括一个数据域data和一个指针域 next。节点结构如图所示:
class Node(object): def __init__(self, data): self.data = data self.next = None
class LinkedQueue(object): def __init__(self): self.front = None
def empty(self): return self.front is None
代码如下:
def push(self, val): new = Node(val) if self.front == None: raise IndexError('队空!') else: cur = self.front # 遍历队列 while cur.next != None: cur = cur.next # 在队尾插入元素 cur.next = new
判断队列是否为空,若队列为空,则抛出异常,否则删除队首节点,代码实现如下
def pop(self): if self.empty(): raise IndexError('队空') else: cur = self.front # 将队首指针指向队首节点的后继节点 self.front = self.front.next return cur
判断队列是否为空,若队列为空,则抛出异常;若队列不为空,则返回队首元素,代码实现如下
def peek(self): if self.empty(): raise IndexError('队空') els: return self.front.data