• 【算法导论】堆排序


    目录

    1. 堆

    1.1 堆的概念

    1.2 堆的分类 

    1.3 堆的性质

    1.4 堆的高度

    2. 维护堆的性质

    2.1 大根堆的维护过程示意图

    2.2 大根堆的维护思路

    2.3 MAX-HEAPIFY函数伪代码

    2.4 以 A[1....n] 为堆的C语言MAX-HEAPIFY函数代码

    3. 建堆

    3.1 建堆思路

    3.2 寻找最后一个父节点

    3.3 建堆算法

    3.4 建堆过程图示 

    4. 堆排序

    4.1 堆排序思路

    4.2 堆排序过程示意图

    4.3 堆排序伪代码

    4.4 C语言完整堆排序代码

    4.5 堆排序时间复杂度

    5. 优先队列

    5.1 优先队列简介

    5.2 优先队列基本操作


    1. 堆

    1.1 堆的概念

            堆是一个数组,它可以被看成一个近似的完全二叉树,树上的每一个节点对应数组中的一个元素。(堆也是一种存储结构,但这里讲的不是)

    逻辑结构:

                                    

    物理结构:

                                

    1.2 堆的分类 

    堆又可以分为:

    • 大根堆: 父节点比子节点的值要大,A [PARENT( i )] ≥ A]
    • 小根堆: 父节点比子节点的值要小,A [PARENT( i )] ≤ Ai ]
    1.3 堆的性质

    1. 若存储堆的数组为A[1......n]则已知某节点 i 可求得其父节点为 ⌊i / 2⌋,左儿子节点为 2i,右儿子节点为 2i + 1.

    2. 若存储堆的数组为A[0......n-1]则已知某节点 i 可求得其父节点为 ⌊(i-1) / 2⌋,左儿子节点为 2i+1,右儿子节点为 2i + 2.

    1.4 堆的高度

    在算法书中定义的堆的高度为:堆中某一结点的高度为该节点到叶节点最长简单路径上边的数目。

    在数据结构书中定义堆的高度:从叶节点那一层到该节点所在层的层数。(比算法书定义的高度多1)

    2. 维护堆的性质

    2.1 大根堆的维护过程示意图

                                            

    2.2 大根堆的维护思路

    1. 维护大根堆性质的函数为MAX-HEAPIFY,在程序每一步中,从A [ i ]、A [ LEFT( i ) ]、A [RIGHT( i )]中选出最大的,并将其下标存储在largest中。

    2. 如果A [ i ]是最大的,那么以 i 为根节点的子树已经是最大堆,程序结束。

    3. 否则最大元素是 i 的某个孩子节点,则交换 A[ i ] 与 A [ largest ] 的值。从而使 i 及其孩子都满足大根堆的性质。

    4. 在交换后,下标 largest 的结点的值是原来的 A [ i ],于是以该节点为根的字数又有可能违反大根堆性质。因此需要对该子树递归调用 MAX-HEAPIFY.

    2.3 MAX-HEAPIFY函数伪代码
    1. MAX-HEAPIFY(A,i)
    2. l = LEFT(i) // 取该节点的左儿子节点
    3. r = RIGHT(i) // 取该节点的右儿子节点
    4. if l<=A.heap-size and A[l] > A[i]
    5. largest = l
    6. else
    7. largest = i
    8. if r<=A.heap-size and A[r] > A[largest]
    9. largest = r
    10. if largest ! = i
    11. exchange A[i] with A[largest]
    12. MAX-HEAPIFY(A,largest)
    2.4 以 A[1....n] 为堆的C语言MAX-HEAPIFY函数代码
    1. void MAX_HEAPIFY(int A[], int i,int heap_size)
    2. {
    3. int l = i * 2;
    4. int r = i * 2 + 1;
    5. int largest = 0;
    6. if (l <= heap_size && A[l] > A[i])
    7. {
    8. largest = l;
    9. }
    10. else {
    11. largest = i;
    12. }
    13. if (r <= heap_size && A[r] > A[largest])
    14. {
    15. largest = r;
    16. }
    17. if (largest != i)
    18. {
    19. swap(A[i], A[largest]);
    20. MAX_HEAPIFY(A, largest, heap_size);
    21. }
    22. }

    时间复杂度:O (h),其中 h 为树的高度。 

    3. 建堆

    3.1 建堆思路

            只需要找到最后一个有子节点的节点(T),然后从该节点到根节点逆向调用MAX_HEAPIFY函数即可。

                                            

    如上图所示,T节点即为元素值为16 的 5 号节点,然后遍历 4->3->2->->1 号节点执行MAX_HEAPIFY。

    3.2 寻找最后一个父节点

    二叉树的度(出度),就是该点所拥有的孩子数。可以分为 n0、n1、n2.

    入度数(边数)=  n0​ + n1 ​+ n2 ​− 1

    出度数(边数)=  0∗n0​ + 1∗n1 ​+ 2∗n2​

    以上两式联立可得 n0​ = n2​ + 1      --->       n2 = n0 -1

    总的节点数 n = n0 + n1 + n2,将 n2 = n0 -1 带入上式可得 n = n0 + n1 + n0 -1 = 2*n0 + n1 - 1

    由此推得:

                                    ​​​​​​​                ​​​​​​​   n_{0} = (n-n_{1}+1)/2

    由于 n1 的数量等于1或0,所以 n0 =  ⌈n / 2⌉,最后一个父节点的数量为 n - n0,综上可以得到最后一个父节点的下标为:⌊n 2⌋

    3.3 建堆算法

    伪代码:

    1. BUILD-MAX-HEAP(A)
    2. Heap-size[A] ← Length[A]
    3. for i = ⌊Length[A]/2⌋ downto 1
    4. doMAX-HEAPIFY(A, i)

    C 语言代码:

    1. void BUILD_MAX_HEAP(int A[],int A_length)
    2. {
    3. int heap_size = A_length;
    4. for (int i = A_length / 2; i >= 1; i--)
    5. {
    6. MAX_HEAPIFY(A, i, heap_size);
    7. }
    8. }

    建堆所用时间:

    3.4 建堆过程图示 

    以以下A数组为例:

    4. 堆排序

    4.1 堆排序思路

    1. 在完成大根堆建立的基础上,我们将 A[ 1 ] 的值与 A[ A.length ] 互换,然后在堆中去掉节点n(通过减少heap_size实现)。

    2. 剩余节点中新的根节点可能会违背大根堆性质,然后我们通过调用MAX-HEAP函数构造一个新的大根堆。

    3. 不断重复此过程,直到堆的大小从 n-1 降到 2。

    4.2 堆排序过程示意图

    4.3 堆排序伪代码
    1. HEAPSORT(A)
    2. BUILD-MAX-HEAP(A)
    3. for i = A.length downto 2
    4. exchange A[1] with A[i]
    5. A.heap-szie = A.heap-size - 1
    6. MAX-HEAPIFY(A,1)
    4.4 C语言完整堆排序代码
    1. # include
    2. using namespace std;
    3. void MAX_HEAPIFY(int A[], int i,int heap_size)
    4. {
    5. int l = i * 2;
    6. int r = i * 2 + 1;
    7. int largest = 0;
    8. if (l <= heap_size && A[l] > A[i])
    9. {
    10. largest = l;
    11. }
    12. else {
    13. largest = i;
    14. }
    15. if (r <= heap_size && A[r] > A[largest])
    16. {
    17. largest = r;
    18. }
    19. if (largest != i)
    20. {
    21. swap(A[i], A[largest]);
    22. MAX_HEAPIFY(A, largest, heap_size);
    23. }
    24. }
    25. void BUILD_MAX_HEAP(int A[],int A_length)
    26. {
    27. int heap_size = A_length;
    28. for (int i = A_length / 2; i >= 1; i--)
    29. {
    30. MAX_HEAPIFY(A, i, heap_size);
    31. }
    32. }
    33. void HEAPSORT(int A[],int A_length)
    34. {
    35. BUILD_MAX_HEAP(A, A_length);
    36. int heap_size = A_length;
    37. for (int i = A_length; i >= 2; i--)
    38. {
    39. swap(A[1], A[i]);
    40. heap_size = heap_size - 1;
    41. MAX_HEAPIFY(A, 1,heap_size);
    42. }
    43. }
    44. int main()
    45. {
    46. int A[11] = { 0,4,1,3,2,16,9,10,14,8,7 };
    47. int A_length = 10;
    48. HEAPSORT(A, A_length);
    49. cout << "heapsort result:";
    50. for (int i = 1; i <= 10; i++)
    51. {
    52. cout << A[i] << " ";
    53. }
    54. return 0;
    55. }

    heapsort result:1 2 3 4 7 8 9 10 14 16

    4.5 堆排序时间复杂度

            调用 BUILD-MAX-HEAP 函数建堆时间花费 O( n ),每调用一次 MAX-HEAPIFY 函数维护堆花费 O(logn),共调用 n - 1次,故,堆排序的时间复杂度为 O(n*logn)。

    5. 优先队列

    5.1 优先队列简介

    堆这一数据结构的基本操作有:

    • 维护堆
    • 建堆
    • 堆排序

    优先队列在堆的基本操作上又添加了:

    • INSERT(S,x):向堆S中插入元素x
    • MAXIMUM(S):返回堆中的最大值
    • EXTRACT-MAX(S):删除并返回堆S中的最大元素
    • INCREASE-KEY(S,x,k):将下标为x处的值改为k

    数据结构包括:逻辑结构、物理结构、基本操作。

    堆和优先队列的基本操作不同,所以堆和优先队列是两种不同的数据结构。

    5.2 优先队列基本操作

    INCREASE-KEY(A, i, key) :O( logn )

    1. INCREASE-KEY(A, i, key)
    2. if key < A[i]
    3. then error "new key is smaller than current key"
    4. A[i] ← key
    5. while i > 1 and A[PARENT(i)] < A[i]
    6. do exchange A[i] ↔ A[PARENT(i)]
    7. i ← PARENT(i)
    INSERT( A , key ) :O( logn )
    1. INSERT(A, key)
    2. heap-size[A] ← heap-size[A] + 1
    3. A[heap-size[A]] ← -∞
    4. INCREASE-KEY(A, heap-size[A], key)
    MAXIMUM( A ) :O( 1 )
    1. HEAP-MAXIMUM(A)
    2. return A[1]
    EXTRACT-MAX( A ) :O( log n )
    1. EXTRACT-MAX(A)
    2. if heap-size[A] < 1
    3. then error "heap underflow"
    4. max ← A[1]
    5. A[1] ← A[heap-size[A]]
    6. heap-size[A] ← heap-size[A] - 1
    7. MAX-HEAPIFY(A, 1)
    8. return max

     INCREASE-KEY(A, i, 15)示意图:

  • 相关阅读:
    【C语言】qsort函数
    RAG之微调垂域BGE的经验之谈
    基于springboot+vue的员工绩效考核与激励系统
    【ATT&CK】MITRE Caldera-路径发现插件
    数据结构-树进阶刷题
    redis为什么要自己实现SDS表示字符串
    ruoyi系统启动
    Java使用Socket简单实现FTP
    EPOLLRDHUP EPOLLHUP 事件
    信息化浪潮下,华为云灾备方案如何保护数据安全
  • 原文地址:https://blog.csdn.net/qq_64585761/article/details/133042288