目录
一、栈(Stack)
-
栈的基本概念
栈是一种线性数据结构,遵循后进先出(Last-In-First-Out,LIFO)原则。最后添加到栈中的元素是第一个被移除的。 -
栈的操作
- 压栈(Push):将元素添加到栈的顶部。
- 出栈(Pop):从栈的顶部移除元素。
- 查看栈顶(Peek):查看栈顶元素,不删除它。
- 判断栈是否为空。
-
示例代码与注释
# 创建一个空栈
stack = []
# 压栈操作
stack.append(1) # 添加元素1到栈
stack.append(2) # 添加元素2到栈
# 出栈操作
top_element = stack.pop() # 移除并返回栈顶元素
# 查看栈顶元素
top_element = stack[-1] # 查看栈顶元素,不删除它
# 判断栈是否为空
is_empty = len(stack) == 0 # 如果栈为空,返回True
4 栈的应用场景
二、 队列(Queue)
-
队列的基本概念
定义:队列是一种线性数据结构,遵循先进先出(First-In-First-Out,FIFO)原则。最早添加到队列中的元素是第一个被移除的。 -
队列的操作
- 入队(Enqueue):将元素添加到队列的尾部。
- 出队(Dequeue):从队列的头部移除元素。
- 查看队头元素(Front):查看队列头部的元素,不删除它。
- 判断队列是否为空。
-
示例代码与注释
from collections import deque
# 创建一个空队列
queue = deque()
# 入队操作
queue.append(1) # 添加元素1到队列尾部
queue.append(2) # 添加元素2到队列尾部
# 出队操作
front_element = queue.popleft() # 移除并返回队头元素
# 查看队头元素
front_element = queue[0] # 查看队头元素,不删除它
# 判断队列是否为空
is_empty = len(queue) == 0 # 如果队列为空,返回True
4 对列的应用场景
三、栈和队列的常见变种与使用
- 栈可以使用数组或链表实现。
- 队列可以使用数组、链表或双端队列(Deque)实现。
3.1 栈的常见的变种与使用
示例:
3.1.1 最小栈(Min Stack)
最小栈(Min Stack)是一种特殊的栈数据结构,除了支持常规的栈操作(入栈和出栈),它还可以高效地获取栈中的最小元素,通常在常数时间内完成。最小栈通常用于需要在栈中保持跟踪最小元素的问题,同时保持常规栈操作的性能。
以下是一个Python示例,演示如何实现一个最小栈:
class MinStack:
def __init__(self):
# 主栈,用于存储元素
self.stack = []
# 辅助栈,用于存储最小元素
self.min_stack = []
def push(self, x):
self.stack.append(x)
# 如果辅助栈为空或新元素小于等于当前最小值,则将新元素入辅助栈
if not self.min_stack or x <= self.min_stack[-1]:
self.min_stack.append(x)
def pop(self):
# 出栈时,如果出栈元素等于当前最小值,也将辅助栈顶元素出栈
if self.stack:
if self.stack[-1] == self.min_stack[-1]:
self.min_stack.pop()
self.stack.pop()
def top(self):
if self.stack:
return self.stack[-1]
def get_min(self):
if self.min_stack:
return self.min_stack[-1]
# 创建最小栈
min_stack = MinStack()
# 入栈操作
min_stack.push(3)
min_stack.push(5)
min_stack.push(2)
min_stack.push(1)
# 获取栈顶元素和最小元素
print(min_stack.top()) # 输出: 1
print(min_stack.get_min()) # 输出: 1
# 出栈操作
min_stack.pop()
print(min_stack.top()) # 输出: 2
print(min_stack.get_min()) # 输出: 2
3.1.2 双栈(Two Stacks)
双栈是由两个栈组成的数据结构,通常用于实现队列或在栈上执行一些复杂操作。
class TwoStacks:
def __init__(self):
# 两个栈,一个用于入队,一个用于出队
self.stack1 = []
self.stack2 = []
def enqueue(self, val):
# 将元素压入stack1,模拟入队操作
self.stack1.append(val)
def dequeue(self):
if not self.stack2:
# 如果stack2为空,将stack1中的元素逐个弹出并压入stack2,以颠倒顺序
while self.stack1:
self.stack2.append(self.stack1.pop())
# 弹出stack2的栈顶元素,模拟出队操作
if self.stack2:
return self.stack2.pop()
3.1.3 固定大小栈(Fixed-Size Stack)
固定大小栈具有固定的容量限制,一旦达到容量上限,无法再添加更多元素。
class FixedSizeStack:
def __init__(self, max_size):
self.max_size = max_size
self.stack = []
def push(self, item):
if len(self.stack) < self.max_size:
self.stack.append(item)
else:
raise IndexError("Stack is full")
def pop(self):
if self.stack:
return self.stack.pop()
else:
raise IndexError("Stack is empty")
def peek(self):
if self.stack:
return self.stack[-1]
def is_empty(self):
return len(self.stack) == 0
def is_full(self):
return len(self.stack) == self.max_size
3.1.4 动态栈(Resizable Stack)
动态可以动态地增加或减少其容量,以适应变化的需求。
class ResizableStack:
def __init__(self):
# 初始容量
self.capacity = 2
# 存储元素的列表
self.stack = [None] * self.capacity
# 当前元素数量
self.size = 0
def push(self, val):
if self.size == self.capacity:
# 如果栈已满,增加容量
self._resize(self.capacity * 2)
# 压栈操作
self.stack[self.size] = val
self.size += 1
def pop(self):
if self.size > 0:
# 出栈操作
self.size -= 1
val = self.stack[self.size]
self.stack[self.size] = None
if self.size <= self.capacity // 4:
# 如果栈的元素数量小于等于容量的四分之一,减小容量
self._resize(self.capacity // 2)
return val
else:
print("Stack is empty. Cannot pop element.")
def _resize(self, new_capacity):
# 调整栈的容量
new_stack = [None] * new_capacity
for i in range(self.size):
new_stack[i] = self.stack[i]
self.stack = new_stack
self.capacity = new_capacity
3.1.5 栈的迭代器
栈的迭代器是一种数据结构,它允许按顺序遍历栈中的元素,而不需要改变栈的状态。这种迭代器通常是通过将栈中的元素复制到另一个数据结构(如列表)来实现的,然后可以在该数据结构上进行迭代操作。
以下是一个示例,演示了如何实现栈的迭代器以及如何使用它来遍历栈中的元素:
class StackIterator:
def __init__(self, stack):
# 接收一个栈作为参数
self.stack = stack
# 复制栈中的元素到列表,以便进行迭代
self.elements = list(stack)
def __iter__(self):
# 返回自身作为迭代器对象
self.current = 0
return self
def __next__(self):
# 迭代下一个元素,直到列表为空
if self.current < len(self.elements):
element = self.elements[self.current]
self.current += 1
return element
else:
# 列表为空,抛出StopIteration异常
raise StopIteration
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
if not self.is_empty():
return self.items.pop()
def is_empty(self):
return len(self.items) == 0
def size(self):
return len(self.items)
# 创建一个栈
stack = Stack()
# 压栈
stack.push(1)
stack.push(2)
stack.push(3)
# 创建栈的迭代器
stack_iterator = StackIterator(stack)
# 使用迭代器遍历栈中的元素
for element in stack_iterator:
print(element)
# 输出:
# 1
# 2
# 3
3.2 队列的常见变种与使用
3.2.1 双端队列(Deque)
双端队列(Deque,Double-ended Queue)是一种具有双向插入和删除操作的数据结构,它允许在队列的两端执行入队和出队操作。双端队列的灵活性使其适用于各种应用场景,包括双向搜索、滑动窗口、实现队列和栈等。
在Python中,可以使用collections
模块中的deque
来创建双端队列。以下是双端队列的基本操作示例:
from collections import deque
# 创建一个双端队列
deque_obj = deque()
# 在队头插入元素
deque_obj.appendleft(1)
deque_obj.appendleft(2)
# 在队尾插入元素
deque_obj.append(3)
deque_obj.append(4)
# 双端队列的内容现在是 [2, 1, 3, 4]
# 在队头出队
front_element = deque_obj.popleft()
print(front_element) # 输出: 2
# 在队尾出队
rear_element = deque_obj.pop()
print(rear_element) # 输出: 4
# 双端队列的内容现在是 [1, 3]
3.2.2 优先队列(Priority Queue)
优先队列(Priority Queue)是一种特殊的队列,其中每个元素都与一个优先级相关联,元素按照优先级的顺序进行出队。在优先队列中,具有较高优先级的元素先出队,而具有较低优先级的元素后出队。这种数据结构通常用于处理需要按照优先级顺序处理的任务或元素。
在Python中,可以使用heapq
模块实现最小堆优先队列,也可以使用第三方库(如queue.PriorityQueue
)来创建优先队列。
以下是一个使用heapq
模块实现最小堆优先队列的示例:
import heapq
class PriorityQueue:
def __init__(self):
self.elements = []
def push(self, item, priority):
heapq.heappush(self.elements, (priority, item))
def pop(self):
if self.elements:
return heapq.heappop(self.elements)[1]
else:
raise IndexError("Priority queue is empty")
# 创建优先队列
pq = PriorityQueue()
# 添加元素到优先队列,带有优先级
pq.push("Task 1", 3)
pq.push("Task 2", 1)
pq.push("Task 3", 2)
# 出队操作将按照优先级顺序执行
print(pq.pop()) # 输出: "Task 2",因为它具有最高优先级
print(pq.pop()) # 输出: "Task 3"
print(pq.pop()) # 输出: "Task 1"
3.2.3 并发队列(Concurrent Queue)
并发队列(Concurrent Queue)是一种队列数据结构,支持多线程并发访问,通常提供线程安全的入队和出队操作。这种队列类型用于多线程环境,以确保多个线程可以安全地访问和修改队列,防止竞态条件和数据损坏。
以下是一个Python示例,演示如何使用Python的queue
模块创建并发队列:
import queue
import threading
import time
# 创建并发队列
concurrent_queue = queue.Queue()
# 定义一个生产者线程,向队列中添加数据
def producer():
for i in range(5):
item = f"Item {i}"
concurrent_queue.put(item)
print(f"Produced: {item}")
time.sleep(1)
# 定义一个消费者线程,从队列中获取数据
def consumer():
while True:
item = concurrent_queue.get()
if item is None:
break
print(f"Consumed: {item}")
concurrent_queue.task_done()
# 启动生产者线程
producer_thread = threading.Thread(target=producer)
producer_thread.start()
# 启动两个消费者线程
consumer_thread1 = threading.Thread(target=consumer)
consumer_thread2 = threading.Thread(target=consumer)
consumer_thread1.start()
consumer_thread2.start()
# 等待生产者线程完成
producer_thread.join()
# 向队列添加特殊的"None"值以通知消费者线程退出
concurrent_queue.put(None)
concurrent_queue.put(None)
# 等待消费者线程完成
consumer_thread1.join()
consumer_thread2.join()
3.2.4 延迟队列(Delay Queue)
延迟队列(Delay Queue)是一种特殊的队列,其中的元素在一定的延迟时间之后才能被出队。通常,延迟队列用于实现任务调度和延迟处理,允许您安排在将来的某个时间执行特定的任务。延迟队列中的每个元素都具有与之关联的延迟时间,当延迟时间过去时,元素才会从队列中出队并执行相关操作。
以下是一个Python示例,演示如何使用延迟队列:
import queue
import threading
import time
# 创建延迟队列
delay_queue = queue.PriorityQueue()
# 定义一个延迟任务
def delayed_task(task, delay):
time.sleep(delay)
print(f"Executing task: {task}")
# 启动延迟任务线程
threading.Thread(target=delayed_task, args=("Task 1", 3)).start()
threading.Thread(target=delayed_task, args=("Task 2", 2)).start()
threading.Thread(target=delayed_task, args=("Task 3", 1)).start()
# 模拟添加延迟任务到队列中
delay_queue.put((time.time() + 5, "Task 4", 5)) # 延迟5秒执行
delay_queue.put((time.time() + 2, "Task 5", 2)) # 延迟2秒执行
delay_queue.put((time.time() + 3, "Task 6", 3)) # 延迟3秒执行
# 出队并执行延迟任务
while not delay_queue.empty():
item = delay_queue.get()
execute_time, task_name, delay = item
current_time = time.time()
if current_time >= execute_time:
print(f"Executing delayed task: {task_name}")
else:
# 如果任务还未到执行时间,重新放回队列
delay_queue.put(item)
time.sleep(1) # 等待1秒后再检查