赞
踩
相较于普通的队列(详见:python实现队列(点击跳转)),循环队列的数据区是固定的(在规定了队列的大小后),而传统的队列的数据区是动态变化的。
而循环队列的本质:固定的数据区域,变动的索引指向
例如:在弹出队首元素的时候,并不会真的将原来的队首元素删除,而是将队首元素的索引变量改变,从而使其指向新的队首元素。如下图所示:
可见并未真正删除原来索引为0的元素(原来的队首),仅仅只是将新的队首的索引赋值给队头变量head
简单来讲就是循环队列的执行效率较高
举个简单的例子:
传统队列的,弹出队首的方法,使用的是列表内置的pop()方法,但是其时间复杂度是O(n),因为当列表中的第一个元素被删除后,其他所有的元素都会向前移动一位。但是循环队列呢?根本不会改变任何的底层中的元素,仅仅只是将索引变一下即可,时间复杂度为O(1),执行效率高,故,在真正使用队列的时候一般都是使用循环队列。
队列顺序表实现的基本框架(基本参数)如下图所示:
head
length
size
item
代码实现如下:
class Queue:
def __init__(self,size):
self.items = [None] * size
self.size = size
self._lenth = 0
self.head = 0
值得注意的是:因为队列的大小是固定的,所以当我们创建顺序表的时候,需要创建一个长度为size的初始化的顺序表才行,如上面的第3行代码。
self.items = [None] * size
基础方法包括:
代码实现如下:
def is_empty(self):
return self._lenth == 0
def length(self):
return self._lenth
坚决不可以用以下代码判断是否为空
def is_empty(self):
return self.items == []
因为循环链表中的顺序表中一直有元素,不为空。
重点内容
)到这里都可以看出,为节点的索引公式为:
i
n
d
e
x
=
s
e
l
f
.
h
e
a
d
+
s
e
l
f
.
_
l
e
n
t
h
待添加值的索引
=
队首索引
+
队列实际长度
i
n
d
e
x
o
f
t
h
e
v
a
l
u
e
t
o
b
e
a
d
d
e
d
=
i
n
d
e
x
o
f
t
h
e
h
e
a
d
o
f
t
h
e
q
u
e
u
e
+
a
c
t
u
a
l
l
e
n
g
t
h
o
f
t
h
e
q
u
e
u
e
index = self.head + self.\_lenth \\待添加值的索引 = 队首索引 + 队列实际长度\\index\ of \ the \ value\ to \ be\ added = index \ of\ the\ head\ of\ the\ queue\ + actual\ length \ of \ the \ queue
index=self.head+self._lenth待添加值的索引=队首索引+队列实际长度index of the value to be added=index of the head of the queue +actual length of the queue
但是真的就这样了?没有其他的情况了?
我们可以看出,我们想给0的位置添加索引,但是算出来的却是8,进行对size取余即可(至于为什么取余,小伙伴自行研究哈!!)
故最终的公式为:
i
n
d
e
x
=
(
s
e
l
f
.
h
e
a
d
+
s
e
l
f
.
_
l
e
n
t
h
)
%
s
e
l
f
.
s
i
z
e
index = (self.head + self.\_lenth)\ \%\ self.size
index=(self.head+self._lenth) % self.size
代码实现:
def push(self,data):
if self._lenth < self.size:
idx = (self._lenth + self.head) % self.size
self.items[idx] = data
self._lenth += 1
else:
print('队列已经满了')
如下图可知,想要弹出索引为2的队首元素3,步骤如下
索引的确定:
n
e
x
t
_
i
t
e
m
_
i
d
x
=
(
s
e
l
f
.
h
e
a
d
+
1
)
%
s
e
l
f
.
s
i
z
e
\\next\_item\_idx = (self.head + 1) \% self.size
next_item_idx=(self.head+1)%self.size
代码实现如下:
def pop(self): # 弹出队首元素
if self.is_empty():
raise ValueError('队列为空')
res = self.items[self.head]
self.head = (self.head + 1 ) % self.size
self._lenth -= 1
return res
返回队首元素索引[self.head]
在items中对应的值即可
def peek(self):
if self.is_empty():
raise ValueError('队列为空')
res = self.items[self.head]
return res
附件中附完整代码
注:由于链表的所有操作的时间复杂度均为O(1),故,没有必要进行循环操作!
附上链表实现队列代码:
''' @Time : 2023-08-07 22:21 @Author : TAGRENLA @File : 队列_链表.py ''' '''用链表实现队列''' # 定义结点类 class Node(): def __init__(self,data,next = None): self.data = data self.next = next class Queue: ''' 链表的第一个元素是队列的头部,链表的最后一个元素是对列的尾部 ''' def __init__(self,size): self.head = None self.rear = None self.lenth = 0 self.size = size def is_empty(self): return self.lenth == 0 def length(self): return self.lenth def push(self,data): ''' 添加一个元素data到队列尾部 创建一个新的结点 将新的结点添加到链表的尾部(链表的尾部既队列的尾部) 将原来的尾结点的next属性指向新的结点 将队列的rear(原来的尾结点)属性指向新的结点 队列长度加1 特殊情况下: 如果链表为空 那么就是添加到链表的头部 具体操作如下: 1.将队列的head属性指向新的结点 2.将队列的rear属性指向新的结点 (因为在空的队列中台添加一个对象那么这个对象既是队首又是队尾) ''' if self.length() == self.size: raise ValueError('队列已经满了') node = Node(data) if self.is_empty(): self.head = node self.rear = node else: self.rear.next = node # 将原来的尾结点的next属性指向新的结点 self.rear = node # 将队列的rear(原来的尾结点)属性指向新的结点 self.lenth += 1 def pop(self): '''抛出队首元素也就是链表的第一个结点''' ''' 创建一个变量指向头节点(用于后续的返回) 将链表的head属性指向原来头节点的下一个结点 ''' if self.is_empty(): raise ValueError('队列为空') value = self.head.data if self.lenth == 1: self.head = None self.rear = None # print(f'队列的头部结点是{self.rear}') else: self.head = self.head.next self.lenth -= 1 return value def peek(self): if self.is_empty(): raise ValueError('队列为空') return self.head.data if __name__ == '__main__': queue = Queue(3) queue.push(1) queue.push(2) queue.push(3) print(queue.pop()) queue.push(4) print(queue.peek()) print(queue.pop()) print(queue.pop()) print(queue.pop())
循环队列顺序表完整代码
class Queue: def __init__(self,size): self.items = [None] * size self.size = size self._lenth = 0 self.head = 0 def is_empty(self): return self._lenth == 0 def length(self): return self._lenth def push(self,data): if self._lenth < self.size: idx = (self._lenth + self.head) % self.size self.items[idx] = data self._lenth += 1 else: print('队列已经满了') def pop(self): # 弹出队首元素 if self.is_empty(): raise ValueError('队列为空') res = self.items[self.head] self.head = (self.head + 1 ) % self.size self._lenth -= 1 return res def peek(self): if self.is_empty(): raise ValueError('队列为空') res = self.items[self.head] return res if __name__ == '__main__': queue = Queue(3) print(queue.is_empty()) print(queue.items) queue.push(1) print(queue.items) queue.push(11) print(queue.items) queue.push(111) print(queue.items) queue.push(1111) print(queue.pop()) print(queue.pop()) print(queue.peek()) print(queue.length()) print(queue.items)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。