数据结构_堆


堆也被称为优先队列。队列中允许的操作是先进先出,在队尾插入元素,在队首取出元素。而堆也是一样,在堆底插入元素,在堆顶取出元素,但是堆中元素的排列不是按照先来后到的顺序,而是按照一定的优先顺序顺序排列的。这个优先顺序可以是元素的大小或者其他规则。如图一所示就是一个堆,堆优先顺序就是最大的元素排在前面,小的元素排在后面,这样得到的堆称为大堆。最大堆中堆顶的元素是整个堆中最大的,并且每一个分支也可以看成一个最大堆。同样的,我们可以定义最小堆。
这里写图片描述

  1. 堆是一个完全二叉树。因为堆中元素按照一定的优先顺序排列,因此可以使用更简单的方法——数组来表示,这样可以节省子节点指针空间,并且可以快速的访问每个节点。堆的数组其实就是堆层级遍历的结果。
  2. 堆有两种, 一种叫小堆(小根堆, 最小堆), 一种叫大堆(大根堆, 最大堆)。
  3. 以小堆为例, 这个树的根节点是这个树中的最小的元素,对于任意一个子树来说, 子树的根节点, 小于左右孩子节点的值。
  4. 以大堆为例, 这个树的根节点是这个树中的最大元素,对于任意一个子树来说, 子树的根节点, 大于左右孩子节点的值。
  5. 在我们不想开辟额外的空间, 或者消耗额外的时间的前提下, 如果我们想进行从小到大排序, 就需要一个大堆;如果我们想进行从大到小排序, 就需要一个小堆。

堆的插入
堆可以看成一个完全二叉树,每次总是先填满上一层,再在下一层从左往右依次插入。(小)堆的插入步骤:

  1. 将新元素增加到堆的末尾。
  2. 按照优先顺序,将新元素与其父节点比较,如果新元素大于父节点则将两者交换位置。
  3. 不断进行第2步操作,直到不需要交换新元素和父节点,或者到达堆顶。相当于节点的上浮过程。
    这里写图片描述

堆的删除

  1. 删除堆顶元素。为了便于重建堆,实际的操作是将最后一个数据的值赋给根节点,然后再从根节点开始进行从上向下的调整。
  2. 调整时先在左右子节点中找最大的,如果父节点比这个最大的节点还大说明不需要调整了,反之将父节和它交换后再考虑后面的节点。相当于根节点数据的“下沉”过程。
    这里写图片描述
    heap.h
#pragma once
#include<stdio.h>
#include<stddef.h>
#include<stdlib.h>
#include<string.h>

#define HeapMaxSize 1000
typedef int HeapType;
typedef int (*Compare)(HeapType a,HeapType b);
typedef struct Heap{
    HeapType data[HeapMaxSize];
    size_t size;
    Compare cmp;
}Heap;
//对堆进行初始化时,决定是大堆还是小堆
void HeapInit(Heap* heap,Compare cmp);
//向堆中插入元素
void HeapInsert(Heap* heap,HeapType value);
//取堆顶元素
int HeapRoot(Heap* heap,HeapType* value);
//删除堆顶元素
void HeapErase(Heap* heap);
//判断堆是否为空
int HeapEmpty(Heap* heap);
//求堆的大小
size_t HeapSize(Heap* heap);
//销毁堆
void HeapDestroy(Heap* heap);
//用数组构建堆
void HeapCreate(Heap* heap,HeapType array[],size_t size);
//对堆进行排序
void HeapSort(HeapType array[],size_t size);

heap.c

#include "heap.h"

//为小堆打造的比较函数
int Less(HeapType a,HeapType b){
    return a < b ? 1 : 0;
}

//为大堆打造的比较函数
int Greater(HeapType a,HeapType b){
    return a > b ? 1 : 0;
}

//对堆进行初始化时,决定是大堆还是小堆
void HeapInit(Heap* heap,Compare cmp){
    if(heap == NULL){
        return;
    }
    heap->size = 0;
    heap->cmp = cmp;
}

//销毁堆
void HeapDestroy(Heap* heap){
    if(heap == NULL){
        return;
    }
    heap->size = 0;
    heap->cmp = NULL;
}

void Swap(HeapType* a,HeapType* b){
    HeapType tmp = *a;
    *a = *b;
    *b = tmp;
}

void AdJustUp(Heap* heap,size_t index){
    if(heap == NULL){
        return;
    }
    size_t child = index;
    size_t parent = (child-1)/2;
    while(child > 0){
        if(!heap->cmp(heap->data[parent],heap->data[child])){
            Swap(&heap->data[parent],&heap->data[child]);
        }else{
            //如果某位置parent和child已满足堆的要求,此时停止上浮,因为上面的节点一定也都时满足堆的要求的
            break;
        }
        child = parent;
        parent = (child-1)/2;
    }
}

//向堆中插入元素
void HeapInsert(Heap* heap,HeapType value){
    if(heap == NULL){
        return;
    }
    if(heap->size >= HeapMaxSize){
        //堆已经满了,无法插入
        return;
    }
    heap->data[heap->size++] = value;
    //对这个堆进行上浮式调整
    //调整的其实位置就是size-1
    AdJustUp(heap,heap->size-1);
}

//取堆顶元素
int HeapRoot(Heap* heap,HeapType* value){
    if(heap == NULL){
        return 0;
    }
    if(heap->size == 0){
        //空堆
        return 0;
    }
    *value = heap->data[0];
    return 1;
}

void AdJustDown(Heap* heap,size_t index){
    if(heap == NULL){
        return;
    }
    size_t parent = index;
    size_t child = index * 2 + 1;
    while(child < heap->size){
        if(child + 1 < heap->size && heap->cmp(heap->data[child+1],heap->data[child])){
            //如果右子树存在,并且右子树的值比左子树更符合堆的要求
            //假设我们这个是大堆,就要求说,如果右子树比左子树大,那么就让child指向右子树
            child = child + 1;
        }
        //child就指向了左右子树中更大的树
        if(heap->cmp(heap->data[child],heap->data[parent])){
            Swap(&heap->data[child],&heap->data[parent]);
        }else{
            break;
        }
        parent = child;
        child = 2 * parent + 1;
    }
}

//删除堆顶元素
void HeapErase(Heap* heap){
    if(heap == NULL){
        return;
    }
    if(heap->size == 0){
        //空堆
        return;
    }
    //交换堆顶元素和最后一个元素
    Swap(&heap->data[0],&heap->data[heap->size-1]);
    //--size进行尾删
    --heap->size;
    //从根节点出发,进行下沉调整
    AdJustDown(heap,0);
}

//判断堆是否为空
int HeapEmpty(Heap* heap){
    if(heap == NULL){
        return 0;
    }
    if(heap->size == 0){
        return 1;
    }
    return 0;
}

//求堆的大小
size_t HeapSize(Heap* heap){
    if(heap == NULL){
        return 0;
    }
    if(heap->size == 0){
        return 0;
    }
    return heap->size;
}

//构建堆
void HeapCreate(Heap* heap,HeapType array[],size_t size){
    if(heap == NULL){
        return;
    }
    //遍历数组,把数组元素依次插入到堆中
    size_t i = 0;
    for(;i < size;i++){
        HeapInsert(heap,array[i]);
    }
}

//实现堆排序,升序排序,从根节点开始删除再插入最后,所以需要一个大堆
void HeapSort(HeapType array[],size_t size){
    //把数组构建成堆,循环对堆进行删除
    Heap heap;
    HeapInit(&heap,Greater);
    HeapCreate(&heap,array,size);
    while(heap.size > 0){
        HeapErase(&heap);
    }
    //循环结束之后,堆排序就完成了
    memcpy(array,heap.data,size*sizeof(HeapType));
}

/////////////////////////////////////////
////////以下为测试代码
/////////////////////////////////////////

#define TEST_HEADER printf("\n=========================%s=========================\n",__FUNCTION__);
void TestInit(){
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    printf("size expected is 0,actual %lu\n",heap.size);
    printf("cmp expected is %p,actual %p\n",Greater,heap.cmp);
    int a = 0;
    printf("&a=%p\n",&a);
    int* p = (int*)malloc(sizeof(int));
    printf("p=%p\n",p);
}

void HeapPrintChar(Heap* heap,const char* msg){
    printf("[%s] \n",msg);
    size_t i = 0;
    for(;i < heap->size;i++){
        printf("[%c|%lu] ",heap->data[i],i);
    }
    printf("\n");
}
void TestInsert(){
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    HeapInsert(&heap,'c');
    HeapInsert(&heap,'b');
    HeapInsert(&heap,'a');
    HeapInsert(&heap,'e');
    HeapInsert(&heap,'f');
    HeapInsert(&heap,'d');
    HeapPrintChar(&heap,"往堆中插入六个元素");
}

void TestRoot(){
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    HeapInsert(&heap,'c');

    HeapInsert(&heap,'b');
    HeapInsert(&heap,'a');
    HeapInsert(&heap,'e');
    HeapInsert(&heap,'f');
    HeapInsert(&heap,'d');
    HeapPrintChar(&heap,"往堆中插入六个元素");
    HeapType value;
    int ret = HeapRoot(&heap,&value);
    printf("ret expected is 1,actual %d\n",ret);
    printf("value expected is f,actual %c\n",value);
}

void TestErase(){
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    HeapInsert(&heap,'c');
    HeapInsert(&heap,'b');
    HeapInsert(&heap,'a');
    HeapInsert(&heap,'e');
    HeapInsert(&heap,'f');
    HeapInsert(&heap,'d');
    HeapPrintChar(&heap,"往堆中插入六个元素");
    HeapErase(&heap);
    HeapPrintChar(&heap,"删除堆顶元素");
}

void TestEmpty(){
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    HeapInsert(&heap,'c');
    HeapInsert(&heap,'b');
    HeapInsert(&heap,'a');
    HeapInsert(&heap,'e');
    HeapInsert(&heap,'f');
    HeapInsert(&heap,'d');
    HeapPrintChar(&heap,"往堆中插入六个元素");
    int ret = HeapEmpty(&heap);
    printf("ret expected is 0,actual is %d\n",ret);
}

void TestSize(){
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    HeapInsert(&heap,'c');
    HeapInsert(&heap,'b');
    HeapInsert(&heap,'a');
    HeapInsert(&heap,'e');
    HeapInsert(&heap,'f');
    HeapInsert(&heap,'d');
    HeapPrintChar(&heap,"往堆中插入六个元素");
    int ret = HeapSize(&heap);
    printf("ret expected is 6,actual is %d\n",ret);
}

void TestSort(){
    TEST_HEADER;
    HeapType array[] = {'d','e','c','a','b'};
    HeapSort(array,sizeof(array)/sizeof(array[0]));
    size_t i = 0;
    for(;i < sizeof(array)/sizeof(array[0]);i++){
        printf("%c ",array[i]);
    }
    printf("\n");
}
int main(){
    TestInit();
    TestInsert();
    TestRoot();
    TestErase();
    TestEmpty();
    TestSize();
    TestSort();
    return;
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值