栈与队列进阶

栈的应用场景

栈的常见操作

1、栈的遍历
遍历栈时需要将栈中的所有元素全部取出,因此需要借助一个辅助栈来实现。
常用的栈的遍历方式有两种:从栈顶到栈底遍历和从栈底到栈顶遍历。
以下是从栈顶到栈底遍历的Python代码实现:

stack = [1, 2, 3, 4, 5]
while stack:
    print(stack.pop())

输出结果为:

5
4
3
2
1

以上代码中,首先定义了一个包含五个元素的列表作为栈,然后使用while循环来不断弹出栈顶元素并打印,直到栈为空。
以下是从栈底到栈顶遍历的Python代码实现:

stack = [1, 2, 3, 4, 5]
for i in range(len(stack)-1, -1, -1):
    print(stack[i])

输出结果为:

5
4
3
2
1

以上代码中,同样定义了一个包含五个元素的列表作为栈,然后使用for循环从栈底开始遍历,依次打印每个元素的值。需要注意的是,在range函数中使用了三个参数,第一个参数表示起始位置,第二个参数表示结束位置(不包含),第三个参数表示步长,这里设置为-1,表示倒序遍历。
无论是哪种遍历方式,都可以通过使用栈的弹出操作来逐个访问栈中的元素。

2、栈的排序
栈排序是指将一个无序的栈中的元素按照一定的规则进行排序,常用的方法为使用另外一个辅助栈来完成排序。
具体实现方法如下:
1.创建一个新的栈作为辅助栈,用于存储已经排好序的元素。
2.从原始栈中弹出一个元素,将其存储在临时变量temp中。
3.如果辅助栈为空,或者辅助栈顶元素小于等于temp,则将temp压入辅助栈。
4.否则,循环弹出辅助栈顶元素并压入原始栈,直到辅助栈为空或者辅助栈顶元素小于等于temp,然后将temp压入辅助栈。
5.重复步骤2~4,直到原始栈为空。
6.最后,辅助栈中存储的元素就是有序的,依次弹出辅助栈中的元素压入原始栈,即可完成排序。
下面是使用Python代码实现栈排序的例子:

def sort_stack(stack):
    tmp_stack = []
    while stack:
        temp = stack.pop()
        while tmp_stack and tmp_stack[-1] > temp:
            stack.append(tmp_stack.pop())
        tmp_stack.append(temp)
    while tmp_stack:
        stack.append(tmp_stack.pop())

# 示例
stack = [3, 4, 1, 5, 2]
sort_stack(stack)
print(stack)
# 输出:[1, 2, 3, 4, 5]

在上面的代码中,使用了两个栈stack和tmp_stack。sort_stack函数接受一个无序栈stack,并调用另外一个辅助栈tmp_stack来完成排序。具体实现方法与上述步骤一致。
经过排序后,原始栈stack中的元素变得有序,其顺序为从小到大。

3、栈的逆置
栈的逆置是指将一个栈中的元素按照相反的顺序重新排列。通常使用另外一个辅助栈来完成逆置操作。
具体实现方法如下:
1.创建一个新的空栈,用于存储逆置后的元素。
2.从原始栈中弹出一个元素,并将其压入新栈中。
3.重复步骤2直到原始栈为空。
4.最后,新栈中存储的元素就是逆置后的顺序。
下面是使用Python代码实现栈逆置的例子

def reverse_stack(stack):
    tmp_stack = []
    while stack:
        tmp_stack.append(stack.pop())
    while tmp_stack:
        stack.append(tmp_stack.pop())

# 示例
stack = [1, 2, 3, 4, 5]
reverse_stack(stack)
print(stack)
# 输出:[5, 4, 3, 2, 1]

在上面的代码中,使用了两个栈stack和tmp_stack。reverse_stack函数接受一个无序栈stack,并调用另外一个辅助栈tmp_stack来完成逆置操作。具体实现方法与上述步骤一致。
经过逆置后,原始栈stack中的元素变成了相反的顺序。

队列的应用场景

1、消息队列
消息队列是一种高效的异步通信机制,通过将消息发送到队列中,实现了不同进程之间的解耦和异步处理。
消息队列包含生产者、消费者和一个存储消息的队列。生产者生成消息并将其放入队列,而消费者从队列中获取消息并进行处理。这种机制可以使得生产者和消费者之间的速度和处理能力独立运行,从而提高整个应用程序的性能和稳定性。
常见的消息队列系统有RabbitMQ、Kafka、ActiveMQ等,下面我们以Python标准库中的queue模块为例,演示如何使用消息队列。

import queue
import threading

def producer(q):
    for i in range(5):
        q.put(i)
        print(f"Produced {i}")
    q.put(None)  # 发送结束信号

def consumer(q):
    while True:
        item = q.get()
        if item is None:  # 收到结束信号
            break
        print(f"Consumed {item}")

q = queue.Queue()
t1 = threading.Thread(target=producer, args=(q,))
t2 = threading.Thread(target=consumer, args=(q,))
t1.start()
t2.start()
t1.join()
t2.join()

在上述代码中,我们创建了两个线程,一个用于生产消息(producer),另一个用于消费消息(consumer)。生产者循环生成5个消息,并将它们放入队列中,最后发送结束信号(None)。消费者从队列中获取消息进行处理,直到收到结束信号。
运行上述代码会输出类似如下的结果:```

```python
Produced 0
Consumed 0
Produced 1
Consumed 1
Produced 2
Consumed 2
Produced 3
Consumed 3
Produced 4
Consumed 4

从输出可以看出,生产者和消费者之间并没有直接的交互,而是通过队列来传递消息。这种机制能够很好地解耦和处理不同进程之间的通信问题。

2、任务调度
使用队列来存储任务可以实现对任务的调度和执行,这种机制通常称为消息队列模式。在这种模式下,任务被添加到队列中,然后由工作进程异步地从队列中获取任务并执行。

下面是一个简单的Python程序,演示如何使用队列来实现任务调度和执行:

import queue
import threading
import time

def worker(q):
    while True:
        task = q.get()
        if task is None:
            break
        print(f"Processing {task}")
        time.sleep(1)
        q.task_done()

q = queue.Queue()
for i in range(3):
    t = threading.Thread(target=worker, args=(q,))
    t.daemon = True
    t.start()

for task in range(5):
    q.put(task)

q.join()
print("All tasks completed")

上述代码中,我们定义了一个worker函数,它从队列中获取任务并执行。每个任务在执行前会等待1秒钟,模拟任务执行的耗时。当队列为空且所有任务都已完成时,主线程输出"All tasks completed"。
接下来,我们创建了3个工作线程,并启动它们。然后,将5个任务分别添加到队列中。在所有任务都执行完之前,主线程会一直等待,即调用q.join()方法阻塞,直到队列中所有任务都被处理完毕。
运行上述代码会输出类似如下的结果:

Processing 0
Processing 1
Processing 2
Processing 3
Processing 4
All tasks completed

从输出可以看出,所有任务都被异步地交给工作线程来执行,并且任意一个工作线程可以获取队列中的任务进行处理。这种机制非常适合于高并发和分布式系统的场景。

队列的常见操作

1、循环队列
循环队列是一种特殊的队列,它可以有效地利用数组实现的队列空间。与普通队列不同的是,循环队列在队列首尾相连的时候,队列空间并没有浪费掉。
具体来说,在循环队列中,队列头和队列尾都可以指向数组的任意位置,当队列到达数组末尾时,队列尾可以回到数组开头,从而形成一个环状结构。这样一来,可以充分利用数组空间,减少空间的浪费。
下面是一个简单的Python程序,演示如何使用数组实现循环队列:

class CircularQueue:
    def __init__(self, max_size):
        self.max_size = max_size
        self.queue = [None] * max_size
        self.head = 0
        self.tail = 0
        self.size = 0

    def is_empty(self):
        return self.size == 0

    def is_full(self):
        return self.size == self.max_size

    def enqueue(self, value):
        if self.is_full():
            raise Exception("Queue is full")
        self.queue[self.tail] = value
        self.tail = (self.tail + 1) % self.max_size
        self.size += 1

    def dequeue(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        value = self.queue[self.head]
        self.head = (self.head + 1) % self.max_size
        self.size -= 1
        return value

queue = CircularQueue(5)
for i in range(5):
    queue.enqueue(i)
assert queue.is_full()

try:
    queue.enqueue(5)
except Exception as e:
    print(e)

for _ in range(5):
    value = queue.dequeue()
    print(value)
assert queue.is_empty()

在上述代码中,我们定义了一个循环队列类CircularQueue,它包含最大长度max_size、队列容器queue、队列头和队列尾分别对应的下标head和tail、以及当前队列元素个数size。其中is_empty和is_full方法用于判断队列是否为空或已满;enqueue和dequeue方法用于插入和删除队列元素。

接下来,我们创建了一个最大长度为5的循环队列,并将5个元素依次插入到队列中,之后再试图插入一个元素,但由于队列已满,因此会抛出异常。然后,我们依次从队列中取出元素,并输出结果。

运行上述代码会输出类似如下的结果:

Queue is full
0
1
2
3
4

从输出可以看出,循环队列是一种高效利用数组空间的数据结构,它可以避免普通队列在队列头和队列尾相连时造成的空间浪费。

2、阻塞队列
阻塞队列是一种特殊的队列,它可以在队列已满或队列为空时阻塞等待,直到有新元素插入或者有元素被取出。通常情况下,阻塞队列被广泛应用于多线程程序中,用于控制线程间的同步和通信。
下面是一个简单的Python程序,演示如何使用阻塞队列实现线程间的同步和通信:

import queue
import threading

# 创建一个阻塞队列
q = queue.Queue(maxsize=5)

# 生产者线程函数
def producer():
    for i in range(10):
        q.put(i)
        print("producer put", i)
    q.put(None)  # 结束标志

# 消费者线程函数
def consumer():
    while True:
        item = q.get()
        if item is None:  # 收到结束标志
            break
        print("consumer get", item)
    q.task_done()  # 完成任务

# 创建生产者和消费者线程,并启动它们
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()

# 等待生产者和消费者线程结束
t1.join()
t2.join()

# 阻塞等待队列中的所有任务完成
q.join()

在上述代码中,我们使用Python提供的queue模块创建了一个大小为5的阻塞队列,并定义了一个生产者线程和一个消费者线程。生产者线程负责向队列中插入元素,而消费者线程则不断从队列中取出元素并进行处理。
在程序运行时,我们首先启动生产者线程和消费者线程,然后等待它们结束。最后,我们调用队列的join方法阻塞等待队列中的所有任务完成。
运行上述代码会输出类似如下的结果:

producer put 0
producer put 1
consumer get 0
producer put 2
consumer get 1
producer put 3
consumer get 2
producer put 4
consumer get 3
producer put 5
consumer get 4
producer put 6
consumer get 5
producer put 7
consumer get 6
producer put 8
consumer get 7
producer put 9
consumer get 8
consumer get 9

从输出可以看出,当队列已满或队列为空时,队列的put和get方法会自动阻塞等待,直到有新元素插入或者有元素被取出。这种特性可以非常方便地实现线程间的同步和通信,并且避免了线程之间的忙等待。

3、优先队列
优先队列是一种特殊的队列,它允许插入具有优先级信息的元素,并以优先级为关键字进行插入和删除操作。通常情况下,优先队列被广泛应用于任务调度、事件处理等场景中。
在Python中,可以使用heapq模块实现优先队列。heapq模块提供了一些heap操作函数,其中包括heappush、heappop、heapify等。我们可以利用这些函数构建一个简单的优先队列。
下面是一个使用heapq模块实现优先队列的Python程序:

import heapq

# 定义一个封装元素和优先级的类
class PriorityQueueItem:
    def __init__(self, item, priority):
        self.item = item
        self.priority = priority
    
    # 重载小于运算符
    def __lt__(self, other):
        return self.priority < other.priority
    
# 定义一个优先队列类
class PriorityQueue:
    def __init__(self):
        self.heap = []

    def push(self, item, priority):
        pq_item = PriorityQueueItem(item, priority)
        heapq.heappush(self.heap, pq_item)

    def pop(self):
        pq_item = heapq.heappop(self.heap)
        return pq_item.item

pq = PriorityQueue()
pq.push("task1", 2)
pq.push("task2", 1)
pq.push("task3", 3)

print(pq.pop())
print(pq.pop())
print(pq.pop())

在上述代码中,我们首先定义了一个封装元素和优先级的类PriorityQueueItem,其中重载了小于运算符,用于比较元素的优先级。然后,我们定义了一个优先队列类PriorityQueue,它包含一个heap数组,用于存储元素,并提供了push和pop方法,实现插入和删除操作。
接下来,我们创建了一个优先队列pq,并向其中插入了3个任务,每个任务都具有不同的优先级。最后,我们依次从优先队列中取出任务,并输出结果。
运行上述代码会输出类似如下的结果:

task2
task1
task3

从输出可以看出,优先队列按照元素的优先级进行了排序,并且每次弹出队首元素的时候,总是弹出优先级最高的元素。这种特性可以非常方便地实现任务调度等场景中的优先级问题。

以上是针对大佬的一下建议,进行的一些小的补充,希望大家多多指教!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值