堆排序(C语言)

本文深入解析了堆排序算法,包括其核心概念、堆调整过程、最大堆创建及排序步骤。通过图文并茂的方式,详细介绍了如何将数组转换为二叉树,进行最大堆调整,创建最大堆,最终实现堆排序。

数据结构总目录

堆排序

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或大于)它的父节点。

1. 图文解析

一、数组转换二叉树,从上往下、从左往右对应数组下标,则对于任意结点下标k
父结点下标 =【(k - 1)/ 2】、左孩子下标 =【2k + 1】、右孩子下标 =【2k + 2】
在这里插入图片描述

二、最大堆调整(Heapify)
从左右孩子中选择最大的孩子结点,与父结点交换,并递归调整交换后的孩子结点
例如如下图,对结点【4】进行最大堆调整,选择最大孩子结点【6】进行交换,交换后递归调整孩子结点【4】往下继续查找最大孩子结点【7】并进行交换
在这里插入图片描述

三、创建最大堆(CreateHeap)
但我们发现单对一个结点进行最大堆调整并不能使得整个二叉树形成最大堆;
所以我们需要对末尾子结点的父结点到根节点依次向前进行最大堆调整,从而使得整个二叉树形成最大堆
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、堆排序(HeapSort)
(1)根结点为最大值,则接下来需要将根结点与末尾子结点交换,即可完成最大值【9】的排序
(2)对根结点进行最大堆调整(已交换的末尾结点不参与调整),使得根结点为下一个最大值
(3)最后通过不断的交换,缩小最大堆调整的范围到只剩下根结点后,即可完成整个序列的排序
在这里插入图片描述
在这里插入图片描述

2. 源代码

#include <stdio.h>
#define size 10

void Swap(int *num, int i, int j)
{
    int temp;
    temp = num[i];
    num[i] = num[j];
    num[j] = temp;
}

// 最大堆调整
void Heapify(int *num, int len, int k)
{
    if (k < len)
    {
        int root = k;           // 根结点
        int lchild = 2*k + 1;   // 左孩子结点
        int rchild = 2*k + 2;   // 右孩子结点
        // 查找左右孩子结点中的最大结点
        if (lchild < len && num[root] < num[lchild])
        {
            root = lchild; 
        }
        if (rchild < len && num[root] < num[rchild])
        {
            root = rchild;
        }
        
        // 交换最大结点到根结点
        if (root != k)
        {
            Swap(num, root, k);
            // 每次交换都可能影响到对应孩子结点子树的顺序
            // 所以需要对交换后的孩子结点子树进行最大堆调整
            Heapify(num, len, root);
        }
    }
}

// 创建最大堆
void CreateHeap(int *num, int len)
{
    int i;
    // 最后一个结点下标
    int last = len - 1;   
    // 最后一个结点的父结点下标      
    int parent = (last - 1) / 2;
    // 从最后一个结点的父结点到根结点,依次进行最大堆调整
    for (i = parent; i >= 0; i--)
    {
        Heapify(num, len, i);
    }
}

// 堆排序
void HeapSort(int *num, int len)
{
    // 创建最大堆并进行最大堆调整
    CreateHeap(num, len);
    printf("最大堆调整!\n");
    int i;
    // 依次取出根结点(最大值)
    for (i = len - 1; i >= 0; i--)
    {
        // 将最大堆的根结点(最大值)换到最后一个结点
        Swap(num, i, 0);
        // 交换后二叉树的根结点发生了改变,故还需对根结点做最大堆调整(已交换的末尾结点不参与调整)
        // 而此时根结点小于所有父结点,因而在调整时只需考虑最大孩子的分支即可
        Heapify(num, i, 0); 
    }   
}

int main()
{
    int i, num[size] = {8, 4, 3, 1, 6, 9, 5, 7, 2, 0};
    HeapSort(num, size);

    for (i = 0; i < size; i++)
    {
        printf("%d ", num[i]);
    }
    printf("\n");
    return 0;
}

3. 测试结果

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

堆排序是一种高效的排序算法,其核心思想是利用堆数据结构进行排序。堆是一种完全二叉树结构,并满足堆性质:父节点的值总是大于或等于(最大堆)或者小于或等于(最小堆)其子节点的值。 ### 堆排序实现步骤 1. **构建堆**:将输入数组构建成一个最大堆。 2. **排序过程**: - 将堆顶元素与堆末尾元素交换。 - 重新调整堆以保持堆性质。 - 重复上述两步操作直到整个数组有序。 堆排序的时间复杂度为 $ O(n \log n) $,且具有原地排序的特点,不需要额外的空间[^2]。 ### C语言实现代码示例 以下是一个完整的C语言程序,实现了堆排序算法: ```c #include <stdio.h> // 交换两个整数的值 void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } // 调整堆,使其满足最大堆的性质 void max_heapify(int arr[], int start, int end) { int dad = start; int son = dad * 2 + 1; // 左子节点索引 while (son <= end) { // 找到较大的子节点 if (son + 1 <= end && arr[son] < arr[son + 1]) { son++; } // 如果父节点比子节点大,则满足堆性质,直接退出循环 if (arr[dad] > arr[son]) { return; } else { // 否则交换父子节点,并继续向下调整 swap(&arr[dad], &arr[son]); dad = son; son = dad * 2 + 1; } } } // 堆排序主函数 void heap_sort(int arr[], int len) { // 构建最大堆 for (int i = len / 2 - 1; i >= 0; i--) { max_heapify(arr, i, len - 1); } // 进行排序 for (int i = len - 1; i > 0; i--) { swap(&arr[0], &arr[i]); // 将当前堆顶元素移到末尾 max_heapify(arr, 0, i - 1); // 调整剩余部分为最大堆 } } // 主函数测试 int main() { int arr[] = {64, 34, 25, 12, 22, 11, 90}; int len = sizeof(arr) / sizeof(arr[0]); printf("Original array:\n"); for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } printf("\n"); heap_sort(arr, len); printf("Sorted array:\n"); for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } ``` ### 输出结果 运行以上程序后,输出如下: ``` Original array: 64 34 25 12 22 11 90 Sorted array: 11 12 22 25 34 64 90 ``` ### 算法特点 - **时间复杂度**:$ O(n \log n) $,适用于大规模数据排序。 - **空间复杂度**:$ O(1) $,原地排序,不占用额外内存。 - **稳定性**:堆排序是不稳定排序算法,因为堆调整过程中可能会改变相同元素的位置[^2]。 ---
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小-黯

谢谢老板Thanks♪(・ω・

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值