[西门子] 你可能不知道的C语言排序神器:堆排序

[复制链接]
查看195 | 回复0 | 2024-6-28 08:05:42 | 显示全部楼层 |阅读模式

C语言的魔法书:揭秘stdio.h

如何用<ctype.h>判断和转换字符类型

01
本节重点
C语言堆排序-数组的应用



堆排序是什么?



堆排序是一种基于堆这种数据结构的排序算法。堆是一种特殊的二叉树,它的每个节点都满足以下性质:
  • 大顶堆:每个节点的值都大于或等于其子节点的值
  • 小顶堆:每个节点的值都小于或等于其子节点的值
这样的性质保证了堆的根节点(堆顶)是整个堆中的最大值或最小值。因此,堆排序就是利用这个特点,不断地把堆顶元素和堆尾元素交换,然后调整堆的结构,直到堆中只剩下一个元素为止。

堆排序的原理

堆排序的原理可以分为两个步骤:
  • 建堆:把一个无序的数组转化为一个堆
  • 排序:把堆中的元素按照大小顺序输出




建堆:

建堆的过程是从数组的最后一个非叶子节点开始,依次向上调整每个节点,使其满足堆的性质。这个过程可以用一个循环来实现,如下:

// 假设数组a的长度为n,下标从0开始void build_heap(int a[], int n) {    // 从最后一个非叶子节点开始,向上调整    for (int i = n / 2 - 1; i >= 0; i--)     {        // 调用shift_down函数,使以i为根的子树满足堆的性质        shift_down(a, i, n);    }}

排序:排序的过程是不断地把堆顶元素和堆尾元素交换,然后缩小堆的大小,再调整堆的结构,直到堆中只剩下一个元素为止。这个过程也可以用一个循环来实现,如下:
// 假设数组a的长度为n,下标从0开始void heap_sort(int a[], int n) {    // 先建堆    build_heap(a, n);    // 循环n-1次,每次把堆顶元素和堆尾元素交换,然后缩小堆的大小,再调整堆的结构    for (int i = n - 1; i > 0; i--)     {        // 交换堆顶元素和堆尾元素        swap(a, 0, i);        // 缩小堆的大小        n--;        // 调用shift_down函数,使以0为根的子树满足堆的性质        shift_down(a, 0, n);    }}
shift_down函数

shift_down函数的作用是调整以某个节点为根的子树,使其满足堆的性质。具体的做法是比较该节点和其左右子节点的值,如果该节点的值不满足堆的性质,就和其较大(或较小)的子节点交换,然后递归地调整交换后的子节点为根的子树,直到该节点的值满足堆的性质或者没有子节点为止。这个过程可以用一个递归函数来实现,如下:
// 假设数组a的长度为n,下标从0开始,i是要调整的节点的下标void shift_down(int a[], int i, int n) {    // 计算左右子节点的下标    int left = 2 * i + 1;    int right = 2 * i + 2;    // 如果左子节点存在,且左子节点的值大于父节点的值(大顶堆的情况)    if (left < n && a[left] > a)     {        // 交换左子节点和父节点的值        swap(a, left, i);        // 递归地调整左子节点为根的子树        shift_down(a, left, n);    }    // 如果右子节点存在,且右子节点的值大于父节点的值(大顶堆的情况)    if (right < n && a[right] > a)     {        // 交换右子节点和父节点的值        swap(a, right, i);        // 递归地调整右子节点为根的子树        shift_down(a, right, n);    }}


swap函数

swap函数的作用是交换数组中两个元素的值。这个函数可以用指针来实现,如下:

// 假设数组a的长度为n,下标从0开始,i和j是要交换的元素的下标void swap(int a[], int i, int j){    // 定义两个指针,分别指向要交换的元素    int *p = &a;    int *q = &a[j];    // 定义一个临时变量,用于存储其中一个元素的值    int temp;    // 把p指向的元素的值赋给temp    temp = *p;    // 把q指向的元素的值赋给p指向的元素    *p = *q;    // 把temp的值赋给q指向的元素    *q = temp;}






堆排序的特点

堆排序的特点有以下几点:


  • 堆排序是一种原地排序算法,即不需要额外的空间来存储数据,只需要在原数组上进行操作即可。
  • 堆排序是一种不稳定排序算法,即可能会改变相同元素的相对顺序。例如,如果数组中有两个相同的元素,它们可能会在排序过程中被交换,导致它们的顺序发生变化。
  • 堆排序的时间复杂度
    O(nlogn)
    ,即无论数组是有序还是无序,堆排序都需要
    O(nlogn)
    的时间来完成排序。这是因为建堆的过程需要
    O(n)
    的时间,而每次交换和调整堆的过程需要
    O(logn)
    的时间,共需要进行
    n−1
    次交换和调整,所以总的时间复杂度为
    O(nlogn)
  • 堆排序的空间复杂度
    O(1)
    ,即只需要常数个额外的空间来存储临时变量,不需要额外的数组或其他数据结构来辅助排序。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册哦

x
您需要登录后才可以回帖 登录 | 注册哦

本版积分规则