队列的特性:先进先出
队列实现,python内置队列
import queue
q = queue.Queue()
# 入队
q.put()
# 队列长度
q.qsize()
# 出队
q.get()
1.层次遍历
例1-1:内置函数实现层次遍历
'''
0619 02队列
二叉树的层次遍历
FIFO队列用Qsize表示
每一层开始处理之前记录
一个while,一个for
'''
import queue
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution(object):
def levelOrder(self, root):
res = []
q = queue.Queue() # 一个队列来保存
# 初始化根节点加入队列中
if root:
q.put(root)
while q.qsize() > 0: # 这一步是层次遍历
q_size = q.qsize() # 当前层里面节点的个数
cur_level = []
for i in range(q_size): # 获得q_size后要顺序遍历获取下一层节点
cur = q.get()
cur_level.append(cur.val)
if cur.left:
q.put(cur.left)
if cur.right:
q.put(cur.right)
# 将下一层节点放到队列中
res.append([x for x in cur_level])
return res
if __name__ == '__main__':
root = TreeNode(3)
root.left = TreeNode(9)
root.right = TreeNode(8)
root.right.left = TreeNode(6)
root.right.right = TreeNode(7)
s = Solution()
print(s.levelOrder(root))
例1-2:两个数组实现层次遍历
'''
0619 02队列
二叉树的层次遍历
FIFO队列用两层level list表示
'''
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution(object):
def levelOrder(self, root):
ans = [] # 存放每一层的结果
cur = [] # 存放当前层的结果
# 初始化
if root:
cur.append(root)
while len(cur): # 相当于q_size
next_level = [] # 存放下一层的节点
cur_val = [] # 存放当前层的值
for i in cur: # 顺序遍历
# 循环当前队列
cur_val.append(i.val)
if i.left:
next_level.append(i.left)
if i.right:
next_level.append(i.right)
cur = next_level # 获取当前层的元素个数——分层遍历
ans.append([x for x in cur_val])
return ans
'''
1.q_size用表示当前层的数组长度
2.如何让队首出列,将值保存,再用新的队列替换旧队列
3.每一层都要初始化
q_size = 1
res = []
q = [root]
while q_size > 0 and root.left and root.right:
cur = q[0]
q.pop(0)
r = [cur.val]
if cur.left:
q.append(cur.left)
if cur.right:
q.append(cur.right)
q_size -= 1
q_size = len(q)
res.append(r)
return res
'''
if __name__ == '__main__':
root = TreeNode(3)
root.left = TreeNode(9)
root.right = TreeNode(8)
root.right.left = TreeNode(6)
root.right.right = TreeNode(7)
s = Solution()
print(s.levelOrder(root))
2.循环队列
例2:设计一个可以容纳k个元素的循环队列
'''
0619 循环队列
设计一个可以容纳K个元素的循环队列,实现以下接口
1.循环队列重点在于循环使用固定空间
2.难点在于控制好front和rear两个首尾指示器
方法1:K个元素空间,3个变量 front,rear,used
队列为空,used=0
队列为满,used=K
下标在0~K-1的范围内移动
取模:index=i的后面一个=(i+1)%capacity
index=i的前面一个=(i-1+capacity)%capacity
'''
class MyCircularQueue:
# 参数K表示这个循环队列最多只能容纳K个元素
def __init__(self, k: int):
self.front = 0
self.rear = 0
self.used = 0
self.a = [0] * k
self.capacity = k
# 将value放入队列中,成功返回true
def enQueue(self, value: int) -> bool:
if self.isFull():
return False
# 放入新元素
self.a[self.rear] = value
# 队尾指针向后移一位
self.rear = (self.rear + 1) % self.capacity
# 使用标记加一
self.used += 1
return True
# 删除队首元素,成功返回true
def deQueue(self) -> bool:
if self.isEmpty():
return False
# 队首指针向后移一位
self.front = (self.front + 1) % self.capacity
# 使用标记减一
self.used -= 1
return True
# 得到队首元素,如果为空,返回-1
def Front(self) -> int:
if self.isEmpty():
return -1
return self.a[self.front]
# 得到队尾元素,如果为空,返回-1
def Rear(self) -> int:
# rear指针指向的是队尾后面的空位置
if self.isEmpty():
return -1
# 获取rear指针的前一位
tail = (self.rear - 1 + self.capacity) % self.capacity
return self.a[tail]
# 判断循环队列是否为空
def isEmpty(self) -> bool:
return self.used == 0
# 判断循环队列是否满了
def isFull(self) -> bool:
return self.used == self.capacity
if __name__ == '__main__':
obj = MyCircularQueue(5)
param_1 = obj.enQueue(1)
param_2 = obj.deQueue()
param_3 = obj.Front()
param_4 = obj.Rear()
param_5 = obj.isEmpty()
param_6 = obj.isFull()
'''
0619 循环队列
设计一个可以容纳K个元素的循环队列,实现以下接口
方法2:K+1个元素空间,2个变量 front,rear
队列为空,front==rear
队列为满:(rear+1)%capacity==front
下标活动范围0~K
变量越少有利于无锁队列的实现
'''
class MyCircularQueue:
# 参数K表示这个循环队列最多只能容纳K个元素
def __init__(self, k: int):
self.front = 0
self.rear = 0
self.a = [0] * (k + 1)
self.capacity = k + 1
# 将value放入队列中,成功返回true
def enQueue(self, value: int) -> bool:
if self.isFull():
return False
# 放入新元素
self.a[self.rear] = value
# 队尾指针向后移一位
self.rear = (self.rear + 1) % self.capacity
return True
# 删除队首元素,成功返回true
def deQueue(self) -> bool:
if self.isEmpty():
return False
# 队首指针向后移一位
self.front = (self.front + 1) % self.capacity
return True
# 得到队首元素,如果为空,返回-1
def Front(self) -> int:
if self.isEmpty():
return -1
return self.a[self.front]
# 得到队尾元素,如果为空,返回-1
def Rear(self) -> int:
# rear指针指向的是队尾后面的空位置
if self.isEmpty():
return -1
# 获取rear指针的前一位
tail = (self.rear - 1 + self.capacity) % self.capacity
return self.a[tail]
# 判断循环队列是否为空
def isEmpty(self) -> bool:
return self.front == self.rear
# 判断循环队列是否满了
def isFull(self) -> bool:
next_rear = (self.rear + 1) % self.capacity
return next_rear == self.front
if __name__ == '__main__':
obj = MyCircularQueue(5)
param_1 = obj.enQueue(1)
param_2 = obj.deQueue()
param_3 = obj.Front()
param_4 = obj.Rear()
param_5 = obj.isEmpty()
param_6 = obj.isFull()
3.单调队列
例3:滑动窗口的最大值
'''
0619 单调队列 滑动窗口的最大值
要求队列中的元素必须满足单调性
1.入队前队列已经满足单调性
2.入队后任然满足单调性
单调递减队列
1.队首元素是队列中的最大值
2.一个元素被其他元素剔除,则不会再出队
3.一个元素是当前元素的最大值,出队后还会再次出队
出队元素不等于队首元素,什么都不做,出队元素与队首元素相比,相等才出队
入队是扩张单调队列的覆盖范围
出队是控制单调递减队列的覆盖范围
队首元素是覆盖范围的最大值
双端队列实现单调队列
'''
from collections import deque
class Solution(object):
def __init__(self):
# 单调队列使用双端队列来实现
self.Q = deque()
# 单调递减队列新元素入队
def push(self, val):
while self.Q and self.Q[-1] < val:
# 队尾元素比新元素小,队尾元素出队
self.Q.pop()
# 新元素加入队尾
self.Q.append(val)
# 单调递减队首元素出队
def pop(self, val):
# 比较队首元素与要出队元素
if self.Q and self.Q[0] == val:
self.Q.popleft()
def maxSlidingWindow(self, nums, k):
res = []
for i in range(len(nums)):
self.push(nums[i])
if i < k - 1:
continue
res.append(self.Q[0])
self.pop(nums[i - k + 1])
return res
if __name__ == '__main__':
nums = [1, 3, -1, -3, 5, 3]
k = 3
s = Solution()
print(s.maxSlidingWindow(nums, k))
'''
0620 滑动窗口最大值
数组实现单调队列
1.单调递减入队
2.将前k个元素全部入队
3.第k+1元素时,将队首元素放入结果集中
4.第i-k+1的元素出队
'''
class Solution(object):
def maxSlidingWindow(self, nums, k):
# 如果列表为空,返回空数组
if not nums or len(nums) == 0 or k == 0:
return []
# 如果滑动窗口大于数组的长度,返回数组的最大值
if k > len(nums):
return [max(nums)]
q = [] # 单调递减队列
res = [] # 保存结果
for i in range(len(nums)):
while len(q) > 0 and q[-1] < nums[i]:
q.pop() # 删除队尾元素 保证单调性
q.append(nums[i])
# 前k个元素全部入队列
if i < k - 1:
continue
# 到第k+1的元素的时候,取队首元素放入结果数组中
res.append(q[0])
# 删除第i-k+1,如果和队首元素相同删除,如果不同,不操作
if nums[i - k + 1] == q[0]:
q.pop(0) # 删除队首元素
return res
if __name__ == '__main__':
nums = [1, 3, -1, -3, 5, 3]
k = 3
s = Solution()
print(s.maxSlidingWindow(nums, k))
'''
0620 循环队列实现滑动窗口最大值
'''
class Queue(object):
def __init__(self, cap):
self.cap = cap
self.head = 0
self.tail = 0
self.used = 0
self.q = [0] * cap
def backIndex(self):
return (self.tail - 1 + self.cap) % self.cap
def push(self, val):
while self.used > 0 and self.q[self.backIndex()] < val:
self.tail = self.backIndex()
self.used -= 1
self.q[self.tail] = val
self.tail = (self.tail + 1) % self.cap
self.used += 1
def pop(self, val):
if self.used > 0 and self.q[self.head] == val:
self.head = (self.head + 1) % self.cap
self.used -= 1
def front(self):
return self.q[self.head]
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
if not nums or len(nums) < k:
return []
Q = Queue(k + 1)
ans = []
for i in range(0, len(nums)):
Q.push(nums[i])
if i < k - 1:
continue
ans.append(Q.front())
Q.pop(nums[i - k + 1])
return ans