堆与堆排序

:堆是一颗完全二叉树。如果父节点的值小于等于孩子节点的值,则称为小根堆;否则称为大根堆。
堆排序
对小根堆来说,不断取出堆顶元素,取出的堆顶元素就构成了一个非严格递增序列。
小根堆的接口定义与实现:

1.堆的结构

一个线性数组 b a s e base base用于存储元素。堆有最大容量限制 c a p a c i t y capacity capacity以及元素个数 l e n g t h length length

#define HEAPMAXSIZE 128
typedef int HElemType;
typedef struct
{
    HElemType *base;
    int capacity;
    int length;
}MinHeap;

2.建堆并初始化

先建立一个空堆。然后根据给定数据来初始化堆。

// 创建空堆
MinHeap* createMinHeap()
{
    MinHeap *h = malloc(sizeof(MinHeap));
    h->base = (HElemType *)malloc(sizeof(HElemType)*HEAPMAXSIZE);
    if(!h->base)
    {
        return NULL;
    }
    h->capacity = HEAPMAXSIZE;
    h->length = 0;
    return h;
}
// 根据给定数据初始化堆
int initWithData(MinHeap *h, HElemType *arr, int length)
{
    if(length >= h->capacity) return -1;
    for(int i=0;i<length;i++)
    {
        insert(h, &arr[i]);
    }
    return 0;
}

3.添加新元素

将新元素加到末尾。如果新元素的值比父节点小,则上浮,直至其大于等于父节点的值。使堆重新满足小根堆。时间复杂度 O ( l o g n ) O(logn) O(logn)

// 交换两元素的值
void swap(HElemType *a, HElemType *b)
{
    HElemType temp = *a;
    *a = *b;
    *b = temp;
}
// 调整堆尾元素到合适的位置
void upAdjust(MinHeap *h)
{
    int childIndex = h->length - 1;
    int parentIndex = (childIndex-1)/2;

    // 孩子节点值小于父节点时,需上浮调整
    while(childIndex>0 && h->base[childIndex] < h->base[parentIndex])
    {
        // 交换值并更新索引
        swap(&h->base[parentIndex], &h->base[childIndex]);
        childIndex = parentIndex;
        parentIndex = (parentIndex-1)/2;
    }
}

// 添加新元素
int insert(MinHeap *h, HElemType *e)
{
    if(h->length+1 == HEAPMAXSIZE) return -1;//堆满,插入失败
    // 上浮调整新元素
    h->base[h->length++] = *e;
    upAdjust(h);
    return 0;
}

4.取堆顶元素

取堆顶元素后,最好的办法是把堆尾元素放到堆顶。如果该元素大于孩子节点,则下沉调整到合适的位置,使堆重新满足小根堆。时间复杂度 O ( l o g n ) O(logn) O(logn)

// 调整堆顶元素到合适的位置
void downAdjust(MinHeap *h)
{
    int parentIndex = 0;
    int childIndex = 2 * parentIndex + 1;//左孩子索引

    // 父节点值大于子节点时,需要进行下沉调整
    while (childIndex < h->length && h->base[parentIndex] > h->base[childIndex])
    {
        // 孩子节点间比较,父节点与最小的孩子进行交换
        if (childIndex + 1 < h->length && h->base[childIndex + 1] < h->base[childIndex]) {
            childIndex++;
        }
        swap(&h->base[parentIndex], &h->base[childIndex]);

        // 更新父节点索引为交换的孩子索引,并计算出新的孩子索引
        parentIndex = childIndex;
        childIndex = 2 * parentIndex + 1;
    }
}

// 取堆顶元素
int extract(MinHeap *h, HElemType *e)
{
    if(h->length == 0) return -1;//堆空,取堆顶元素失败
    *e = h->base[0];
    // 堆尾元素放到堆顶,然后下沉调整
    h->base[0] = h->base[--h->length];
    downAdjust(h);
    return 0;
}

5.显示堆

// 显示堆
void printMinHeap(MinHeap *h)
{

    for (int i=0; i<h->length; i++)
    {
        printf("%d ", h->base[i]);
    }
    printf("\n");
}

6.销毁堆

// 释放堆空间
void destroy(MinHeap *h)
{
    free(h->base);
    free(h);
}

7.测试代码

对数据序列 11 , 17 , 4 , 10 , 29 , 4 , 18 , 18 , 22 , 1 11,17,4,10,29,4,18,18,22,1 11,17,4,10,29,4,18,18,22,1进行堆排序。
在这里插入图片描述

int main()
{
    HElemType e;
    MinHeap *h = createMinHeap();
    // 用指定数据创建一个最小堆
    int testArr[10] = {11,17,4,10,29,4,18,18,22,1};
    initWithData(h, testArr, sizeof(testArr)/sizeof(testArr[0]));

    printf("capacity:%d\n", h->capacity);
    printf("length:%d\n", h->length);
    printMinHeap(h);
    //取堆顶元素
    int n = h->length;
    for(int i=0; i<n; i++)
    {
        extract(h, &e);
        printf("%d\n", e);
    }
    destroy(h);
    return 0;
}

运行结果:

capacity:100
length:10
1 4 4 17 10 11 18 18 22 29
1
4
4
10
11
17
18
18
22
29

取出的堆顶元素形成了一个非严格递增序列。
堆排序时空复杂度分析
根据给定数据建堆,时间复杂度 O ( l o g n ) O(logn) O(logn),再取堆顶元素,时间复杂度 O ( l o g n ) O(logn) O(logn),故总时间复杂度 O ( l o g n ) O(logn) O(logn)
空间复杂度 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值