排序算法-堆排序

引用&推荐:https://www.bilibili.com/video/BV1Eb41147dK

堆定义

堆,是通常是一个可以被看做一棵完全二叉树的数组对象。

堆特性

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。

完全二叉树数组表示时的特性

完全二叉树可以使用数组表示,树的节点层次遍历存放到数组,则会有如下关系:
若父节点表示为 i,则子节点可以表示为:

left 	= 2 * i + 1;
right 	= 2 * i + 2; 

逆推,如子节点为i,则父节点可以表示为:

root 	= (i - 1) / 2;

堆使用完全二叉树数组表示时的特性

设一维数组为 tree = [], 父节点下标为 root, 左右子节点下标分别为left、right,则满足:

tree[root] > tree[left] && tree[root] > tree[right] 

堆排序

堆排序-不稳定选择排序
时间复杂度:平均=O(nlog n) 最坏=O(nlog n) 最好=O(nlog n)

#include <stdio.h>

/**
 * 辅助打印函数
 */
void print_array(int *a, int len) {
    for (int i = 0; i < len; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");
}

/**
 * 辅助交换函数
 */
void swap(int tree[], int i, int j) {
    int temp = tree[i];
    tree[i] = tree[j];
    tree[j] = temp;
}

/**
 * 堆调整
 * i 表示调整以i为根的子树的堆
 */
void heapify(int tree[], int n, int i) {
    int left    = 2 * i + 1;    //左孩子的数组坐标
    int right   = 2 * i + 2;    //右孩子的数组坐标
    int max     = i;            //默认最大值为根节点

    if ( left < n && tree[left] > tree[max] ) { 
        max = left;             //记录左孩子节点为最大值
    }

    if ( right < n && tree[right] > tree[max] ) {
        max = right;            //记录右孩子节点为最大值
    }

    if (max != i) {             //最大值为子节点时
        swap(tree, i, max);     //调整最大值到根节点
        heapify(tree, n, max);  //破坏了原有子树的堆结构,故而继续调整子树以维护堆的特效
    }
}

/**
 * 把n个乱序的数构建为堆
 * 堆是一个完全二叉树,填充的数据的时候,从上到下,从左往右,都是有序的,故而存在这样的特效
 * 若 i 为父节点,则
 * left  = 2 * i + 1;
 * right = 2 * i + 2;
 * 逆推 子节点为i,则父节点为 (i-1)/2
 * 最后一个节点的坐标 last_node=n-1
 * 最后一个节点的父坐标为 parent=(last_node-1)/2
 */
void build_heap(int tree[], int n) {
    int last_node = n - 1, parent = (last_node - 1) / 2;

    for (int i = parent; i >= 0; i--) {
        heapify(tree, n, i);
    }
}

void heap_sort(int tree[], int n) {
    build_heap(tree, n);    //构建堆
    
    for (int i = n-1; i >= 0; i--) {
        swap(tree, i, 0);   //隐形消除最后一个元素(把最大值放原数组末尾)
        heapify(tree, i, 0);//继续把剩余的元素调整为堆
    }
}

int main() {
    int tree[] = {2, 5, 1, 3, 10, 4};
    print_array(tree, 6);
    heap_sort(tree, 6);
    print_array(tree, 6);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值