1.3.1 队列的数据类型
队列当中没有位置的概念,只要求保证先进先出即可。
1.3.2 队列的数据操作
1.3.2 队列的实现(主要基于线性表的技术)
1.3.2.1 基于链表的实现代码(由于队列实质上是线性表约束下的结构,所以只需要在线性表的基础上修改)
# 链表的基础上实现队列
class Lnote():
def __init__(self,ele=None,next_=None):
self._element=ele
self._next = next_
class Llist():
def __init__(self):
self._head = None
def preappend(self,ele): # 于表头添加元素
self._head = Lnote(ele,self._head)
def out_me(self): # 于表尾弹出元素
p = self._head
if p == None:
print('队列里无元素')
elif p._next == None:
print(p._element)
self._head=None
else:
while p._next is not None:
p0=p
p = p._next
print(p._element)
p0._next = None # 记住一定是改指针p的next而非直接给节点赋值为None
1.3.2.2 基于顺序表的固定容量的队列实现代码
这里需要说明的是:
- 利用list为基础构造的队列,固定循环长度是必要的。有两种循环的方法,①指针到了最末端,指定回到0②用p%len.也可以优雅的循环指针。这里采取了前者。
- 在队头指针与队尾指针相等时,队列为空,当队尾+1刚好等于队头时,队列为满。
- 用指针为引索,给定义好的列表赋值。
# 顺序表实现固定容量的队列结构
class Queuelist():
def __init__(self,max_len=6):
self._inpoint = -1
self._outpoint = -1
self._Queue = list(range(max_len))
self._max_len = max_len
def inqueue(self,x):
if self._inpoint ==self._max_len-2:
self._inpoint = -2
if (self._inpoint +1)==self._outpoint:
raise Exception("队列已满!!!")
if self._inpoint == -2:
self._inpoint=-1
self._inpoint += 1
self._Queue[self._inpoint]=x
def outqueue(self):
if not self._inpoint==self._outpoint:
if self._outpoint ==self._max_len-2:
self._outpoint = -2
if self._inpoint == -2:
self._inpoint=-1
self._outpoint += 1
if self._outpoint == -1:
print('队列为空')
else:
return self._Queue[self._outpoint]
else:
print("队列为空,无法出队")
def is_full(self):
return (self._inpoint+1)==self._outpoint
def is_empty(self):
return self._inpoint==self._outpoin
1.3.2.3 基于list的无限容量队列的实现代码
设计并实现:
- 由于list的自动扩容机制和队列的扩容机制不一致,所以必须设计队列独有的扩容机制
- 会定义新的异常类
- 考虑到 SQueue类里面必须有的变量: _head(队首位置),_elements(元素),_nums(元素个数),_len(list的长度)。
- 数据不变式的概念:类里面的各种属性之间的相互平衡,每个属性的变化所牵涉到的另一个属性的变化规则。只有遵从这里面的关系,才能保证类在操作过程中不会出现问题。满足数据不变式要做到两点,① 初始状态的属性满足 ② 类的操作之后不会破坏。
那么,现在我们考虑设计一个队列的数据不变式的逻辑:
- _head :队首元素的下标,当出队的时候,它的值必然改变
- _nums:队列的元素个数,它实实在在记录着队列中的元素个数,添加队尾元素需要用head+nums确定下标
- _element:定义了不需要扩容时存放的list容器
- _len:记录elements的长度,当len==nums的时候表明,容器满,需扩容
具体实现代码:
class SQueue():
def __init__(self, len_=8):
self._head = 0 # 队首下标
self._len = len_ # 存储区长度
self._elements = [0]*self._len # 元素s
self._nums = 0 # 队列元素个数
def is_empty(self):
return self._nums == 0
def peek(self): # 查看队首元素
if self._nums == 0:
return print('队列为空')
else:
return self._elements[self._head]
def dequeue(self): # 出队
if self._nums == 0:
return print('队列为空,无法出队')
out_id = self._head
self._head += 1
self._nums -= 1
return self._elements[out_id]
def enqueue(self,x):
if self._nums==self._len:
self.extent()
self._elements[(self._head+self._nums)%self._len] = x # 这一个取余数的操作是妙极了
self._nums += 1
def extent(self): # 扩容
old_len = self._len
self._len *= 2
new_elements =[0]*self._len
# 将可能折叠的状态理顺放入新的存储区
for i in range(old_len):
new_elements[i]= self._elements[(self._head+i)%old_len]
self._elements = new_elements
self._head = 0
- 上面的代码有两个地方十分关键:① 很好的保证了数据不变式的关系,使得每一个操作都显得十分和谐 ② (head+nums)
%len 的循环模式 ③ 变存储区的元素对应关系的处理