堆的基本操作

堆的性质

首先, 这个堆和堆栈那个堆没有一点关系
这个堆就是一棵完全二叉树
分为大堆和小堆
满足以下性质
如果是大堆, 那么任意根节点的孩子节点都比它小
这里写图片描述

如果是小堆, 那么任意根节点的孩子节点都比它大
这里写图片描述

堆的基本操作
  • 初始化
  • 插入
  • 删除
  • 取堆顶元素
  • 销毁
  • 堆排序

我们每次插入完或者删除完以后, 这个堆仍然要满足堆的性质, 所以就需要在插入删除以后再做一些操作, 使元素上浮或者下沉, 使其仍然满足堆的性质

由于堆是一棵完全二叉树, 所以它的父子节点有这些关系
定义:
father : 父节点下标
lchild: 左孩子节点的下标
rchild: 右孩子节点的下标
则有:
lchild = father * 2 + 1
rchild = father * 2 + 2
father = (child - 1) / 2
(其中 child 为左右任意孩子的下标, 因为 2/2 = 1, 3/2 = 1)

因为有大小堆的区分, 所以我们需要用回调函数 compare 来制定比较规则, 以此决定构建的是大堆还是小堆

实现代码
/*================================================================

# File Name: heap.h
# Author: rjm
# mail: rjm96@foxmail.com
# Created Time: 2018年05月12日 星期六 14时43分40秒

================================================================*/


#pragma once
// 1. 堆是一个完全二叉树
// 2. 堆有两种, 一种叫小堆(小根堆, 最小堆),
//    一种叫大堆(大根堆, 最大堆).
// 3. 以小堆为例, 这个树的根节点是这个树中的最小的元素
//    对于任意一个子树来说, 子树的根节点, 小于左右孩子节点的值.
// 4. 以大堆为例, 这个树的根节点是这个树中的最大的元素
//    对于任意一个子树来说, 子树的根节点, 大于左右孩子节点的值.

#include <stdio.h>

#define TEST_HEAD printf("\n===============%s=================\n", __FUNCTION__)
#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, Compare cmp);
// 取堆顶元素
int HeapTop(Heap *heap, HeapType *value);
// 删除堆顶元素
void HeapErase(Heap *heap, Compare cmp);
// 清空堆
int HeapEmpty(Heap *heap);
// 求堆的大小
size_t HeapSize(Heap *heap);
// 销毁堆
void HeapDestroy(Heap *heap);
// 向下调整
void AdjustDown();
// 打印堆
void printHeap();

// 在我们不想开辟额外的空间, 或者消耗额外的时间的前提下,
// 如果我们想进行从小到大排序, 就需要一个大堆
// 如果我们想进行从大到小排序, 就需要一个小堆
void HeapSort(HeapType array[], size_t size, Compare cmp);
/*================================================================
# File Name: heap.c
# Author: rjm
# mail: rjm96@foxmail.com
# Created Time: 2018年05月11日 星期五 14时09分13秒
================================================================*/
#include <stdio.h>
#include <string.h>
#include "heap.h"

//堆: 是一棵完全二叉树
//最大堆: 每个根节点的值都大于左右孩子节点的值
//最小堆: 每个根节点的值都小于左右孩子节点的值


int cmp_max(HeapType a, HeapType b)
{
    return a > b ? 1 : 0;
}
int cmp_min(HeapType a, HeapType b)
{
    return a < b ? 1 : 0;
}
//交换数组元素的函数
void Swap(int a, int b, Heap* heap)
{
    HeapType tmp = heap->data[a];
    heap->data[a] = heap->data[b];
    heap->data[b] = tmp;
}
//初始化堆
void HeapInit(Heap* heap, Compare cmp)
{
    heap->size = 0;
    cmp = NULL;
}
//插入元素
void HeapInsert(Heap* heap, HeapType value, Compare cmp)
{
    if(heap == NULL)
    {
        return ;
    }
    if(heap->size == HeapMaxSize)
    {
        //堆已经满了
        return ;
    }
    //否则, 先将其插入堆的最后, 下标为size
    //再将其上浮, 直到满足堆的规则
    heap->data[heap->size] = value;
    int cur = heap->size;
    int father = (cur-1)/2;
    while(cur > 0 && cmp( heap->data[cur], heap->data[father] ) == 1)
    {
        Swap(cur, father, heap);
        cur = father;
        father = (cur-1)/2;
    }
    ++heap->size;
}
// 取堆顶元素
int HeapTop(Heap *heap, HeapType *value)
{
    if(heap == NULL)
    {
        //堆为空
        return 0;
    }
    *value = heap->data[0];
    return 1;
}
// 删除堆顶元素
void HeapErase(Heap *heap, Compare cmp)
{
    if(heap == NULL)
    {
        return ;
    }
    if(heap->size == 0)
    {
        //空堆
        return ;
    }
    //交换堆顶元素和最后一个元素
    Swap(0, (heap->size)-1, heap);
    //size--, 删除最后一个元素
    --heap->size;
    //然后再进行调整, 使其满足堆的性质
    AdjustDown(heap, 0, cmp);
}

//清空堆
int HeapEmpty(Heap *heap)
{
    if(heap == NULL)
      //空堆
      return 0;
    heap->size = 0;
    return 1;
}
//求堆的大小
size_t HeapSize(Heap *heap)
{
    if(heap == NULL)
      //空堆
      return 0;
    return heap->size;
}
//销毁堆
void HeapDestroy(Heap *heap)
{
    if(heap == NULL)
      return ;
    heap->size = 0;
}
//将一个数组变为一个堆
void Array_to_Heap(Heap* heap, HeapType* arr, int arr_size, Compare cmp)
{
    if(arr == NULL)
      return ;
    for(int i=0; i<arr_size; i++)
    {
        HeapInsert(heap, arr[i], cmp);
    }
    memcpy(arr, heap->data, sizeof(HeapType)*arr_size);
}
// 堆排序
// 在我们不想开辟额外的空间, 或者消耗额外的时间的前提下,
// 如果我们想进行从小到大排序, 就需要一个大堆
// 如果我们想进行从大到小排序, 就需要一个小堆
void HeapSort(HeapType array[], size_t size, Compare cmp)
{
    if(array == NULL)
      return ;
    Heap heap;
    HeapInit(&heap, cmp);
    Array_to_Heap(&heap, array, size, cmp);
    printHeap(&heap, "堆排序");
    HeapType value;
    for(int i=0; i<size; i++)
    {
        HeapTop(&heap, &value);
        HeapErase(&heap, cmp);
        array[i] = value;
    }
}

//向下调整
void AdjustDown(Heap* heap, int index, Compare cmp)
{
    if(index < 0)
      return ;
    int tmp = 0;//用来临时保存最小的值的下标
    int IsNeedAdjust_flag = 0;//判断是否需要调整的标志
    //当前节点有孩子 并且 需要调整时, 进入循环
    while(index*2+1 < heap->size && IsNeedAdjust_flag == 0)
    {
        //如果右子树存在, 先比较左右子树的大小
        if(index*2+2 < heap->size)
        {
            if(cmp(heap->data[index*2+1], heap->data[index*2+2]) == 1)
            {
                tmp = index*2+1;
            }
            else
            {
                tmp = index*2+2;
            }
            if(cmp(heap->data[index], heap->data[tmp]) == 0)
            {
                Swap(index, tmp, heap);
                index = tmp;
            }
            else
                IsNeedAdjust_flag = 1;
        }
        //如果右子树不存在, 就和左子树比较
        else
        {
            if(cmp(heap->data[index], heap->data[index*2+1]) == 0)
            {
                Swap(index, index*2+1, heap);
                index = index*2+1;
            }
            else
              IsNeedAdjust_flag = 1;
        }
    }
}

//打印堆
void printHeap(Heap* heap, char* msg)
{
    printf("\n=====%s=====\n", msg);
    if(heap == NULL)
      return ;
    for(int i=0; i<heap->size; i++)
    {
        printf("[%2d|%2d] ", i, heap->data[i]);
    }
    printf("\n");
}
/*********************************************************
  测试代码
 *********************************************************/
//测试堆的创建
void TestHeapInsert()
{
    TEST_HEAD;
    Heap heap;
    HeapInit(&heap, cmp_min);
    HeapInsert(&heap, 1, cmp_min);
    HeapInsert(&heap, 3, cmp_min);
    HeapInsert(&heap, 0, cmp_min);
    HeapInsert(&heap, 5, cmp_min);
    HeapInsert(&heap, 4, cmp_min);
    HeapInsert(&heap, 7, cmp_min);
    HeapInsert(&heap, 9, cmp_min);
    HeapInsert(&heap, 8, cmp_min);
    HeapInsert(&heap, 6, cmp_min);
    HeapInsert(&heap, 2, cmp_min);
    printHeap(&heap, "往堆中插入4个元素");
}
//测试删除堆顶元素
void TestHeapDeleteTop()
{
    TEST_HEAD;
    Heap heap;
    HeapInit(&heap, cmp_min);
    HeapInsert(&heap, 1, cmp_min);
    HeapInsert(&heap, 2, cmp_min);
    HeapInsert(&heap, 3, cmp_min);
    HeapInsert(&heap, 4, cmp_min);
    HeapErase(&heap, cmp_min);
    printHeap(&heap, "删除一次堆顶元素");
    HeapErase(&heap, cmp_min);
    HeapErase(&heap, cmp_min);
    printHeap(&heap, "再删除两次堆顶元素");
    HeapErase(&heap, cmp_min);
    printHeap(&heap, "再删除一次堆顶元素");
    HeapErase(&heap, cmp_min);
    printHeap(&heap, "对空堆删除");
}
//测试将一个数组变为一个堆
void TestArrToHeap()
{
    TEST_HEAD;
    HeapType arr[] = {9, 1, 8, 2, 7, 3, 6, 4, 0, 5};
    int size = sizeof(arr)/sizeof(arr[0]);
    printf("size = %d\n", size);
    Heap heap;
    HeapInit(&heap, cmp_min);
    Array_to_Heap(&heap, arr, size, cmp_max);
    for(int i=0; i<size; i++)
    {
        printf("[%2d|%2d] ", i, arr[i]);
    }
}

//测试获取堆顶元素
void TestHeapTop()
{
    TEST_HEAD;
    Heap heap;
    HeapInit(&heap, cmp_min);
    HeapInsert(&heap, 1, cmp_min);
    HeapInsert(&heap, 2, cmp_min);
    HeapInsert(&heap, 3, cmp_min);
    HeapInsert(&heap, 4, cmp_min);
    HeapType value;
    HeapTop(&heap, &value);
    printf("expect 1, actual %d\n", value);
}

//测试堆排序
void TestHeapSort_min()
{
    TEST_HEAD;
    HeapType arr[] = {1, 3, 0, 5, 4, 7, 9, 8, 6, 2};
    //HeapType arr[] = {9, 1, 8, 2, 7, 3, 6, 4, 0, 5};
    int size = sizeof(arr)/sizeof(arr[0]);

    HeapSort(arr, size, cmp_min);

    for(int i=0; i<size; i++)
    {
        printf("[%d]", arr[i]);
    }
}
void TestHeapSort_max()
{
    TEST_HEAD;
    HeapType arr[] = {1, 3, 0, 5, 4, 7, 9, 8, 6, 2};
    //HeapType arr[] = {9, 1, 8, 2, 7, 3, 6, 4, 0, 5};
    int size = sizeof(arr)/sizeof(arr[0]);

    HeapSort(arr, size, cmp_max);

    for(int i=0; i<size; i++)
    {
        printf("[%d]", arr[i]);
    }
}
int main()
{
    TestHeapInsert();
    TestHeapDeleteTop();
    TestHeapTop();
    TestArrToHeap();
    TestHeapSort_min();
    TestHeapSort_max();

    printf("\n");
    printf("\n");
    printf("\n");
    return 0;
}
测试结果

这里写图片描述

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值