青训营X豆包MarsCode 技术训练营第三课 | 豆包MarsCode AI 刷题

103 阅读5分钟

这篇文章,我在豆包MarsCode AI刷题(代码练习)题库中队列分类下选取一道中等题、两道困难题进行解析。

队列的知识点

队列(queue)是一种具有「先进入队列的元素一定先出队列」性质的表。由于该性质,队列通常也被称为先进先出(first in first out)表,简称 FIFO 表。队列支持的操作有:

  • 插入元素
  • 删除元素
  • 访问队首
  • 访问队尾
  • 清空队列

特殊的队列(双端队列)

双端队列是指一个可以在队首/队尾插入或删除元素的队列。相当于是栈与队列功能的结合。双端队列支持的操作有 4 个:

  • 在队首插入一个元素
  • 在队尾插入一个元素
  • 在队首删除一个元素
  • 在队尾删除一个元素

Python 队列 列表(不推荐使用)

普通列表可以作为队列,但从性能角度来看并不理想。由于在起始位置插入或删除元素需要将所有其他元素都移动一个位置,因此需要的时间为0(n),因此不推荐在Python中使用列表作为队列使用。

q = []
q.append('a')
q.append('b')
q.append('c')
print(q) # ['a', 'b', 'c']
q.pop()  # 结果为'a'
print(q) # ['b', 'c']

Python 队列 collections.deque

这个库中的deque类实现了一个双端队列,支持在O(1)的时间中从任意端添加和删除元素。由于deque队列和栈的结合体,所以deque既可用作队列也可用作栈。

from collections import deque
q = deque()
q.append('a')
q.append('b')
q.append('c')
print(q)     # ['a', 'b', 'c']
q.popleft()  # 结果为'a'
print(q)     # ['b', 'c']
q.popright() # 结果为'c'
print(q)     # ['b']

Python 队列 queue.Queue

这个库中的qeque类实现了一个队列,在Python标准库中以同步的方式实现,提供了锁语义来支持多个并发的生产者和消费者,支持在O(1)的时间中从队尾添加元素和从队首删除元素。

from queue import Queue
q = Queue()
q.put('a')
q.put('b')
q.put('c')
print(q) # ['a', 'b', 'c']
q.get()  # 结果为'a'
print(q) # ['b', 'c']

Python 队列 multiprocessing.Queue

multiprocessing.Queue作为共享作业队列来实现,允许多个并发worker并行处理队列中的元素。由于CPython中存在全局解释器锁(GIL),因此无法在单个解释器进程上执行某些并行化过程,使得大家都转向基于进程的并行化。

from multiprocessing import Queue
q = Queue()
q.put('a')
q.put('b')
q.put('c')
print(q) # ['a', 'b', 'c']
q.get()  # 结果为'a'
print(q) # ['b', 'c']

注意事项

在Python中比较推荐使用 collections.deque作为实现FIFO队列数据结构和LIFO栈数据结构的最佳选择。

在Cpp中可以直接使用queue stack deque,这里就不详细介绍Cpp中的STL库函数了,如果有兴趣,可以自己去了解一下。

中等题

小R的随机播放顺序

问题描述

小R有一个特殊的随机播放规则。他首先播放歌单中的第一首歌,播放后将其从歌单中移除。如果歌单中还有歌曲,则会将当前第一首歌移到最后一首。这个过程会一直重复,直到歌单中没有任何歌曲。

例如,给定歌单 [5, 3, 2, 1, 4],真实的播放顺序是 [5, 2, 4, 1, 3]

保证歌曲中的id两两不同。

思路

直接使用collections.deque

代码

from collections import deque
def solution(n: int, a: list) -> list:
    result = []
    q = deque(a)

    while q:
        result.append(q.popleft())
        if q:
            q.append(q.popleft())
    return result

困难题

查找热点数据问题

问题描述

给你一个整数数组 nums 和一个整数 k,请你用一个字符串返回其中出现频率前 k 高的元素。请按升序排列。

你所设计算法的时间复杂度必须优于 O(n log n),其中 n 是数组大小。

思路

说实话,在Python中有很多定义好的库函数给我们使用,非常方便

直接使用 collections.counter

代码

from collections import Counter
def solution(nums, k):
    count = Counter(nums)
    result = []
    for x in count:
        result.append(x)
        if len(result) >= k: break
    result.sort()
    return result

小Q奇偶操作数组

问题描述

小Q有一个长度为 nn 的数组,他可以进行 kk 次操作,每次操作可以对数组中的任意一个数字执行以下操作:

  1. 如果选择的数字 xx 是奇数,则将 xx 乘以 2,即 x=x×2x=x×2。
  2. 如果选择的数字 xx 是偶数,则将 xx 乘以 2 再加 1,即 x=x×2+1x=x×2+1。

小Q想知道,经过 kk 次操作后,数组的元素之和最小可以是多少。

思路

这题,我们使用heapq创建一个优先队列(最小堆),然后每次取出最前面的那个元素,再添加回队列中。

代码

def solution(n: int, k: int, a: list) -> int:
    current_sum = sum(a)
    # 创建一个优先队列(最小堆),用于存储数组中的元素
    import heapq
    heapq.heapify(a)
    
    # 进行 k 次操作
    for _ in range(k):
        # 弹出当前最小的元素
        smallest = heapq.heappop(a)
        
        # 如果最小元素是奇数,乘以2
        if smallest % 2 == 1:
            new_value = smallest * 2
        # 如果最小元素是偶数,乘以2再加1
        else:
            new_value = smallest * 2 + 1
        
        # 更新当前和
        current_sum += new_value - smallest
        
        # 将新值重新加入堆中
        heapq.heappush(a, new_value)
    
    return current_sum

总结

队列这个知识点下面的题目,只要你能看出这能用队列实现,那这就是一道简单题!