堆排序详解

堆排序

堆:基于完全二叉树的特殊数据结构

  • 逻辑结构:
    • 大顶堆
      •  根节点的值必须大于他的所有的孩子节点,对于二叉树中的所有子树都满足这个规律
    • 小顶堆
      • 根节点的值必须小于他的所有的孩子节点,对于二叉树中的所有子树都满足这个规律

数据存放在一个堆上,任意的节点,都要满足小顶堆
        数据放到堆上        insert   怎么插入 能够满足堆属性
        最小值  找根节点    extract  调整 满足小顶堆

堆特点:没有顺序,很方便找最大/最小值  TOP10

=========================================================================

存储结构:
         顺序存储        [i/2, i, 2i, 2i+1]
         n个数,申请n+1个节点,从1号索引开始存数据

操作:  插入数据  删除数据。

  1. 插入数据思路:上移
    1. 在最后一个位置,插入新元素
    2. 找这个元素的父节点,若父节点的值大
    3. 那么就与父节点的值进行交换
    4. 重复二三步,直到没有父节点比本身大,就结束循环
  2. 删除数据思路:下移
    1. 把根节点的值备份,作为被提取的值,好做返回值
    2. 把最后一个元素放到根节点的位置上,然后有效长度减一,删除最后一个元素
    3. 将节点和左右孩子中最小的值进行比较
    4. 如果节点的值小于那个最小值,就不换,否则重复第三四步
//miniHeap.h
#ifndef SORT_MINIHEAP_H
#define SORT_MINIHEAP_H
/* 最小堆结构,使用完全二叉树来存储,使用顺序存储 */
#include "../sortHelper.h"
// 最小堆的结构
typedef struct {
    keyType *data;			// 用顺序存储动态保存堆的数据
    int len;				// 约束堆data域的长度
    int capacity;			// 最大容量
}MiniHeap;

// 产生n个元素的堆空间
MiniHeap *createMiniHeap(int n);
void releaseMiniHeap(MiniHeap *heap);
// 插入元素
void insertMiniHeap(MiniHeap *heap, keyType e);
// 提取元素
keyType extractMiniHeap(MiniHeap *heap);
#endif //SORT_MINIHEAP_H
//
// Created by ASUS on 2023/9/11.
//

#include "miniHeap.h"
#include <stdlib.h>
#include <string.h>
MiniHeap *createMiniHeap(int n)
{
    MiniHeap *heap = (MiniHeap *) malloc(sizeof(MiniHeap));
    if (heap == NULL) {
        return NULL;
    }
    heap->data = (keyType *) malloc(sizeof(keyType) * (n + 1));
    if (heap->data == NULL) {
        return NULL;
    }
    memset(heap->data, 0, sizeof(keyType) * (n + 1));
    heap->capacity = n;
    heap->len = 0;				// 每次插入元素先加1,再插入
    return heap;
}
void releaseMiniHeap(MiniHeap *heap)
{
    if (heap) {
        if (heap->data) {
            free(heap->data);
            heap->data = NULL;
        }
        free(heap);
    }
}
// ---------------- 二叉堆的插入操作 --------------------
static void shiftUp(MiniHeap *heap, int k)
{
    keyType tmp;
    while(k>1&&heap->data[k/2]>heap->data[k])
    {
        tmp=heap->data[k];
        heap->data[k]=heap->data[k/2];
        heap->data[k/2]=tmp;
        k/=2;
    }
}
/* 先在最后位置插入元素,然后通过上移操作,确定这个新元素的位置,保证每个根节点是子节点的最小值 */
void insertMiniHeap(MiniHeap *heap, keyType e)
{
    if(heap->len+1>heap->capacity)
    {
        printf("已经满了,再也装不下了\n");
        return;
    }
    heap->data[++heap->len]=e;
    // 上移操作
    shiftUp(heap,heap->len);
}

static void shiftDown(MiniHeap *heap, int k)
{
    keyType tmp;
    while(2*k<=heap->len)// 保证有左孩子
    {
        int index=2*k;
        if(index + 1 <= heap->len&&heap->data[index]>heap->data[index+1])
        {
            index+=1;//如果有右孩子,右孩子和左孩子比一下,右小,那么假设要更新
        }
        if(heap->data[k]<heap->data[index])break;
        // 发现当前值比左右孩子最小的值还要大
        tmp=heap->data[k];
        heap->data[k]=heap->data[index];
        heap->data[index]=tmp;
        k=index;
    }
}
/* 取出堆顶元素,把堆中的最后一个元素放到堆顶,根据原则下沉 */
keyType extractMiniHeap(MiniHeap *heap)
{
    if(heap->len<=0)
    {
        printf("没有啦,真的一点都没有啦\n");
        return 0;
    }
    keyType ret=heap->data[1];
    heap->data[1]=heap->data[heap->len];
    heap->len--;
    shiftDown(heap,1);
    return ret;
}
//heapSort.h
#ifndef SORT_HEAPSORT_H
#define SORT_HEAPSORT_H
#include "miniHeap.h"

void miniHeapSort(SortTable *table);
#endif //SORT_HEAPSORT_H

#include "heapSort.h"


void miniHeapSort(SortTable *table)
{
    MiniHeap *miniHeap = createMiniHeap(table->length);
    for (int i = 0; i < table->length; ++i) {
        insertMiniHeap(miniHeap, table->data[i].key);
    }
    for (int i = 0; i < table->length; ++i) {
        table->data[i].key = extractMiniHeap(miniHeap);
    }
    releaseMiniHeap(miniHeap);
}
#include "miniHeap.h"
#include "heapSort.h"

void test01() {
    int data[] = {9, 3, 7, 6, 5, 1, 10, 2};
    int n = 20;
    MiniHeap *miniHeap = createMiniHeap(n);
    for (int i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
        insertMiniHeap(miniHeap, data[i]);
    }
    printf("array: ");
    for (int i = 1; i <= miniHeap->len; ++i) {
        printf("\t%d", miniHeap->data[i]);
    }
    printf("\nextra: ");
    // for (int i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
    // 	printf("\t%d", extractMiniHeap(miniHeap));
    // }
    keyType tmp = extractMiniHeap(miniHeap);
    while (tmp) {
        printf("\t%d", tmp);
        tmp = extractMiniHeap(miniHeap);
    }
    releaseMiniHeap(miniHeap);
}
// 随机排序表的堆排序测试
void test02() {
    int n = 1000000;
    SortTable *table = generateRandomArray(n, 0, n);
    testSort("HeapSort: ", miniHeapSort, table);
    releaseSortTable(table);
}

int main() {
    test02();
    return 0;
}

 如果想更加详细了解堆排序,不妨可以看看STL源码剖析中的heap算法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值