c语言中如何实现堆排序

发布时间:2021-07-07 14:45:16 作者:Leah
来源:亿速云 阅读:173
# C语言中如何实现堆排序

## 1. 堆排序概述

堆排序(Heap Sort)是一种基于二叉堆数据结构的比较排序算法。它由J. W. J. Williams于1964年提出,具有以下特点:

- **时间复杂度**:平均和最坏情况下均为O(n log n)
- **空间复杂度**:O(1)(原地排序)
- **稳定性**:不稳定排序
- **适用性**:适合大数据量排序,尤其当需要部分排序时效率很高

## 2. 堆的基本概念

### 2.1 二叉堆的定义
二叉堆是一种特殊的完全二叉树,满足以下性质:
- **结构性质**:完全二叉树的结构
- **堆序性质**:
  - 最大堆:父节点值 ≥ 子节点值
  - 最小堆:父节点值 ≤ 子节点值

### 2.2 堆的存储
由于堆是完全二叉树,通常用数组存储:
- 对于节点i(从0开始):
  - 父节点:(i-1)/2
  - 左子节点:2*i+1
  - 右子节点:2*i+2

## 3. 堆排序算法步骤

堆排序主要分为两个阶段:

1. **建堆(Heapify)**:将无序数组构建成堆
2. **排序**:反复取出堆顶元素,调整剩余元素为新堆

### 3.1 完整算法流程
```c
void heapSort(int arr[], int n) {
    // 1. 构建最大堆
    for (int i = n/2 - 1; i >= 0; i--)
        heapify(arr, n, i);
    
    // 2. 逐个提取元素
    for (int i = n-1; i > 0; i--) {
        swap(&arr[0], &arr[i]); // 将当前最大移到末尾
        heapify(arr, i, 0);    // 对剩余元素重新堆化
    }
}

4. 核心操作:堆化(Heapify)

4.1 堆化函数实现

// 对以i为根的子树进行堆化,n是堆的大小
void heapify(int arr[], int n, int i) {
    int largest = i;        // 初始化最大元素为根
    int left = 2 * i + 1;   // 左子节点
    int right = 2 * i + 2;  // 右子节点

    // 如果左子节点大于根
    if (left < n && arr[left] > arr[largest])
        largest = left;

    // 如果右子节点大于当前最大值
    if (right < n && arr[right] > arr[largest])
        largest = right;

    // 如果最大值不是根
    if (largest != i) {
        swap(&arr[i], &arr[largest]);
        // 递归堆化受影响的子树
        heapify(arr, n, largest);
    }
}

4.2 交换函数

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

5. 完整C语言实现

#include <stdio.h>

// 交换函数
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 堆化函数
void heapify(int arr[], int n, int i) {
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    if (left < n && arr[left] > arr[largest])
        largest = left;

    if (right < n && arr[right] > arr[largest])
        largest = right;

    if (largest != i) {
        swap(&arr[i], &arr[largest]);
        heapify(arr, n, largest);
    }
}

// 堆排序主函数
void heapSort(int arr[], int n) {
    // 构建最大堆(从最后一个非叶子节点开始)
    for (int i = n / 2 - 1; i >= 0; i--)
        heapify(arr, n, i);

    // 逐个提取元素
    for (int i = n - 1; i > 0; i--) {
        swap(&arr[0], &arr[i]);
        heapify(arr, i, 0);
    }
}

// 打印数组
void printArray(int arr[], int n) {
    for (int i = 0; i < n; ++i)
        printf("%d ", arr[i]);
    printf("\n");
}

// 测试代码
int main() {
    int arr[] = {12, 11, 13, 5, 6, 7};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("原始数组: \n");
    printArray(arr, n);

    heapSort(arr, n);

    printf("排序后数组: \n");
    printArray(arr, n);
    
    return 0;
}

6. 算法复杂度分析

6.1 时间复杂度

6.2 空间复杂度

7. 堆排序的优化

7.1 非递归实现堆化

递归实现可能产生栈溢出风险,可用循环替代:

void heapify(int arr[], int n, int i) {
    int largest, left, right;
    
    while (1) {
        largest = i;
        left = 2 * i + 1;
        right = 2 * i + 2;
        
        if (left < n && arr[left] > arr[largest])
            largest = left;
            
        if (right < n && arr[right] > arr[largest])
            largest = right;
            
        if (largest == i) break;
            
        swap(&arr[i], &arr[largest]);
        i = largest;
    }
}

7.2 构建堆的优化

Floyd提出的自底向上建堆方法效率更高

8. 堆排序的应用场景

  1. 需要O(n log n)时间且空间受限时

    • 相比快速排序,堆排序最坏情况也是O(n log n)
    • 相比归并排序,不需要额外空间
  2. 需要获取前k个最大/最小元素时

    • 只需部分排序时效率很高
  3. 优先级队列实现

    • 堆是优先级队列的理想数据结构

9. 堆排序的局限性

  1. 不稳定排序:相同元素可能改变相对顺序
  2. 缓存不友好:跳跃访问模式导致缓存命中率低
  3. 常数因子较大:实际运行时间可能比快速排序慢

10. 与其他排序算法比较

算法 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性
堆排序 O(n log n) O(n log n) O(1) 不稳定
快速排序 O(n log n) O(n²) O(log n) 不稳定
归并排序 O(n log n) O(n log n) O(n) 稳定
插入排序 O(n²) O(n²) O(1) 稳定

11. 扩展:实现最小堆排序

只需修改堆化条件即可实现升序排序:

void heapify_min(int arr[], int n, int i) {
    int smallest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    if (left < n && arr[left] < arr[smallest])
        smallest = left;

    if (right < n && arr[right] < arr[smallest])
        smallest = right;

    if (smallest != i) {
        swap(&arr[i], &arr[smallest]);
        heapify_min(arr, n, smallest);
    }
}

12. 总结

堆排序是一种高效的通用排序算法,特别适合: - 需要保证最坏情况下性能的场景 - 内存受限的环境 - 需要实现优先级队列时

理解堆排序的关键在于掌握: 1. 二叉堆的数据结构特性 2. 堆化操作的实现原理 3. 建堆和排序的两个阶段

通过本文的C语言实现和详细解析,读者应能掌握堆排序的核心思想并能在实际编程中灵活应用。 “`

这篇文章共计约2500字,采用Markdown格式编写,包含: 1. 算法原理说明 2. 完整的C语言实现代码 3. 复杂度分析 4. 优化建议 5. 应用场景和比较 6. 扩展内容等完整的技术细节

推荐阅读:
  1. C语言堆排序问题排查
  2. 堆排序的基本实现

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

c语言

上一篇:PHP框架Laravel中怎么实现supervisor执行异步进程

下一篇:Spring Boot怎么给配置项加密

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》