最大堆、最小堆、堆排序、代码实现。

# Lecture06-Proiority Queues 优先级队列

# 概述

A priority queue is a collection of zero or more elements. Each element has a priority or value.

一个优先级队列是 0 个或者更多元素的集合。每一个元素都有一个优先级或者值

进入队列的时候有优先级,出队列优先出高优先级的.

# 如何确定优先级

以下我们确定元素的优先级是通过数字的大小来确定。

  1. In a min priority queue the find operation finds the element with minimum priority, while the delete operation delete this element.

    在最小优先级队列中,当需要删除一个元素的时候,我们找到优先级最小的元素来删除

  2. In a max priority queue, the find operation finds the element with maximum priority, while the delete operation delete this element.

    在最大优先级队列中,当需要删除一个元素的时候,我们找到优先级最大的元素来删除

# ADT (逻辑上最大优先级队列)

image-20221205130741555

# Heap

A max heap (min Heap):(最大堆)

  1. is A complete binary tree

    最大堆是一个完全二叉树

  2. The value in each node is greater(less) than or equal to those in its children(if any).

    每一个节点上的值都大于 (小于) 或者等于他的子节点 (如果有的话)

# 例子

img

最大堆:节点比自己的每个子节点都大

img

最小堆:节点比自己的每个子节点都笑

注意:完全二叉树可以用矩阵来进行存储。

  • 从上向下一层一层进行记录。

# 最大优先级队列的代码实现

image-20221205131454054

# 构造函数

image-20221205131644445

# 插入算法

image-20221205131713780

image-20221205131734821

首先,将插入元素插入到堆的最后;然后,经过反复操作【和父节点比优先级】,一直比较直到比父节点小为止。

image-20221205132008168

用数组模拟完全二叉树,可以用常数时间复杂度求出它的父节点下标;

为什么堆排序中,i/2 是父节点?

直观感受:

image-20221205132744256

image-20221205132754856

推导:

利用等比数列求和

第 n 行的第 a 个元素在数组中下标为:2n + a - 1

第 n 行的第 2a - 1 和 2a 个元素在数组中的下标为:2n+1 + 2a 和 2n+1 + 2a - 1

# 删除算法

image-20221205132925899

最大堆删除
树根删除,最后一个节点放到树根

下滤:左节点和右节点比较,较大的和父节点相比,如果父节点较大,循环结束,否则,换完继续和子节点比较。

image-20221205133015811

ci < CurrentSize && heap[ci] < heap[ci + 1]

对比一下,左子节点和右子节点哪个更大。跟大的那个进行比较,不断进行下滤的操作。

# minHeap 的实现(cpp 代码实现)

image-20221205142159835

image-20221205142257272

image-20221205142318851

image-20221205142445030

image-20221205142524014

# 初始化一个非空的最大优先级数列(自底向上)

img

把初始指针指向最后一个节点的父结点 (N/2), 然后进行循环,然后每一个都换一遍就完成。

总体来讲是从最后一个节点的父结点开始,对所有的非叶节点进行下滤操作。

image-20221205142925609

image-20221205143139842

# 算法复杂度分析

对于不同层的节点,其下滤的计算量时不同的

如何从感觉上立即这个问题 —— 在数据的开始是不会到 lgn 的,而只有到后面的时候才能达到 lgn(lgn = log2n)

img

i 层需要交换 k-i 次,该层中有 2i 个结点

i 到 根的距离, j 到 叶的距离

O(n) 的算术复杂度

# 自上向下的初始化操作

img

最详细的最小堆构建、插入、删除的过程图解

# 优先级队列的应用

# 堆排序 (容易考)

# 复杂度分析

  1. initialize a max heap with the n elements to be sorted O(n)

    初始化一个 n 个元素的最大堆,O (n)

  2. each time we delete one element, then adjust the heap O(log2n)

    每次我们删除最大的元素,调整堆的时间复杂度为 O (log2n)

  3. Time complexity is O(n)+O(nlog2n)= O(nlog2n)

    对于所有情况,堆排序的复杂度都是 nlgn

# 例子:

25 为什么有个星号?因为数组里面有两个 25

img

删除 49,8 放到根的位置,49 放到 8 的位置。

堆排序每次删除最大的,然后把最大的放到最下方节点,把节点交换到顶部后进行下滤算法。

img

堆排序是不稳定的:因为相同数据的相对位置改变

稳定:25 25 的相对位置不变
不稳定:25 25
的相对位置改变

# 堆排序代码实现(?

image-20221205144226520

image-20221205144440890

image-20221205144457500

堆排序可视化网站:

https://www.cs.usfca.edu/~galles/visualization/HeapSort.html

# The Selection Problem 查找问题

问题描述:在 N 个元素中找出第 K 个最大元素。

  1. 1A 算法:读入 N 个元素放入数组, 并将其选择排序,返回适当的元素。算法时间复杂度:O (N2)
  2. 1B 算法:
    1. 将 K 个元素读入数组,并对其排序 (按递减次序)。最小者在第 K 个位置上。
    2. 一个一个地处理其余元素:每读入一个元素与数组中第 K 个元素 (在 K 个元素中为最小) 比较,如果大于,则删除第 K 个元素,再将该元素放在合适的位置上 (调整过程)。如果小于,则舍弃。最后在数组 K 位置上的就是第 K 个最大元素。
    3. 运行时间 (1B 算法): O (K2 + (N - K)K ) = O( NK ) 当 K = N / 2 (向上取整), O ( N2)
  3. 例如:3, 5, 8, 9, 1, 10,找第 3 个最大元素。

# 4.2.1. 用堆来解决当前问题

  1. 6A 算法:假设求第 K 个最小元素
    1. 将 N 个元素建堆 (最小) O ( N )
    2. 执行 K 次 delete,O (K*logN) O ( N + K * log N )
      1. 如果 K = (N/2)(向上取整),O ( N * log N )
      2. 如果 K = N ,O (N * log N) 堆排序
    3. 如果是 N 取代最后一个是 nlgn,可以考虑使用不同的情况来确定建立最大堆还是最小堆。
  2. 6B 算法:假设求第 K 个最大元素
    1. 读入前 K 个元素, 建立最小堆 O (K)
    2. 其余元素一一读入:每读入一个元素与堆中第 K 个最大元素比 (实际上是堆中最小元素) O (1)
      • 大于,则将小元素去掉 (堆顶),该元素进入,进行一次调整。O (log K )
      • 小于,则舍弃。
    3. O( K + ( N-K) * log K ) = O( N*log K)
    4. 当 K = (N/2)(向上取整) , θ(N * log N )
  3. 对 6A, 6B, 用同样的数据进行测试, 只需几秒钟左右给出问题解。

# 例题:2009 统考题

img

  1. 答案:A
  2. 直接按照顺序一行一行生成。

img