在上一篇贪心算法中,我们涉及到了堆的运用,就来详细地探讨一下堆是什么呢?
堆
堆(Heap)是一种特殊的完全二叉树或近似完全二叉树的数据结构,通常用于实现优先队列。堆的主要特点是能够高效地进行插入和删除操作,同时保持堆的性质不变。堆有两种主要类型:最大堆(Max Heap)和最小堆(Min Heap)。
- 最大堆:每个节点的值都大于或等于其子节点的值。
- 最小堆:每个节点的值都小于或等于其子节点的值。
堆的操作
- 插入:将新元素添加到堆的末尾,然后通过向上调整(Percolate Up 或 Bubble Up)来恢复堆的性质。
- 删除:通常删除的是根节点(最大堆中的最大值或最小堆中的最小值)。删除后,将最后一个节点移到根节点的位置,然后通过向下调整(Percolate Down 或 Bubble Down)来恢复堆的性质。
堆的应用
- 优先队列:堆是实现优先队列的常用数据结构,因为它可以在 O(log n) 时间内完成插入和删除操作。
- 堆排序:堆排序是一种高效的排序算法,利用堆的性质可以在 O(n log n) 时间内完成排序。
- 选择算法:堆可以用于在 O(n) 时间内找到数组中的第 k 大或第 k 小元素。
既然说到了优先队列,我们也来看一下队列是什么结构呢?
队列
队列(Queue)是一种常见的线性数据结构,遵循先进先出(FIFO,First-In-First-Out)的原则。这意味着最早进入队列的元素会最先被移除。队列在很多应用场景中都非常有用,例如任务调度、缓冲区管理和广度优先搜索等。
- 先进先出(FIFO) :队列中的元素按照进入的顺序依次离开队列。
- 两端操作:队列有两个主要端点,前端(front)和后端(rear)。元素从后端入队(enqueue),从前端出队(dequeue)。
队列的主要操作
- 入队(Enqueue) :将新元素添加到队列的尾部。
- 出队(Dequeue) :移除并返回队列头部的元素。
- 获取队头元素(Front) :访问队列头部的元素,但不移除它。
- 获取队尾元素(Rear) :访问队列尾部的元素,但不移除它。
- 检查队列是否为空(IsEmpty) :判断队列是否为空。
- 获取队列长度(Size) :返回队列中元素的数量。
可以简单地理解为,堆有大小顺序地维护,方便我们查找,而队列主要维护的是先后顺序。
下面我们来看一下题目,关于堆的代码实现回去看贪心算法那章吧!
队列题目示例
小R有一个特殊的随机播放规则。他首先播放歌单中的第一首歌,播放后将其从歌单中移除。如果歌单中还有歌曲,则会将当前第一首歌移到最后一首。这个过程会一直重复,直到歌单中没有任何歌曲。
题目解析
我们可以用一个队列来模拟这个过程。具体步骤如下:
- 初始化一个队列,将歌单中的所有歌曲按顺序加入队列。
- 每次从队列中取出第一首歌并播放。
- 如果队列中还有歌曲,将当前的第一首歌移到队列的末尾。
- 重复上述步骤,直到队列为空。
题目解答
def solution(n: int, a: list) -> list:
play_order = [] # 存储播放顺序
while a:
# 播放并移除列表中的第一首歌
play_order.append(a.pop(0))
if a: # 如果列表中还有歌曲
# 将新的列表中的第一首歌移到最后
first_song = a.pop(0)
a.append(first_song)
return play_order