数据结构---堆的基本操作及堆排序

介绍

首先我们要将数据结构中的堆和内存中的堆区区分开来,内存中的堆区是操作系统管理的,和数据结构中的堆没有半毛钱关系。堆只有两种,大堆和小堆。

  • 大堆(大根堆):父节点的值大于左右孩子结点的值,也就是说大堆的根节点是整个堆中的最大值,左右孩子结点的值毫无关系
  • 小堆(小根堆):父节点的值小于左右孩子结点的值,也就是说小堆的根节点是整个堆中的最小值,左右孩子结点的值毫无关系

这里写图片描述

堆的表示

我们通常用一个数组来表示一个堆,数组中存放的是堆的层序遍历结果,第一个元素即根节点
这里写图片描述

实现堆的基本操作

  • 结构体声明
//定义一个比较函数的函数指针,用来指明该堆是小堆还是大堆
typedef int (*Compare)(HeapType a, HeapType b); 

typedef struct Heap{
    HeapType data[HeapMaxSize];//数组表示,存放堆中数据
    size_t size;//表示有效数据的个数
    Compare com;//函数指针,用来决定实现的是小堆还是大堆
}Heap;
  • 比较函数

//初始化的时候将堆初始化成大堆或者小堆(传函数名进去),在进行插入删除操作的时候用到该函数
int Greater(HeapType a, HeapType b)//表示大堆
{
    return a > b?1:0;
}

int Less(HeapType a, HeapType b)//表示小堆
{
    return a < b?1:0;
}
  • 堆的初始化和销毁
//初始化堆
void HeapInit(Heap* heap,Compare cmp)
{
    if(heap == NULL)
    {
        //非法输入
        return;
    }
    heap->size = 0;
    heap->com = cmp;
}
//销毁堆
void HeapDestroy(Heap* heap)
{
    if(heap == NULL)
    {
        return;
    }
    heap->size = 0;
}

  • 堆的插入和删除堆顶元素(堆主要是使用其堆顶元素)操作
    • 堆的插入中最重要的就是上浮操作,意思是每次插入新数据时先放置到数组的末尾,然后和其父节点比较,这时就要用到比较函数,如果满足函数,就不动,不满足就要进行上移,也就是数据交换,直到移动到它合适的位置
    • 堆的删除中最重要的就是下潜操作,先将堆顶元素,也就是0号下标元素和数组末尾元素交换数据,再将堆顶元素按堆的规则(大堆还是小堆)往下移动,直到移动到它合适的位置

可以说 上浮下潜 是堆里最重要的两个操作

//交换函数
void Swap(HeapType *a, HeapType *b)
{
    HeapType tmp = *a;
    *a = *b;
    *b = tmp;
}


//上浮函数
void AdjustUp(Heap* heap, size_t child)
{
    if(heap == NULL)
    {
        return;
    }
    if(child == 0)
    {
        return;
    }
    size_t parents = (child - 1)/2;
    if(!heap->com(heap->data[parents],heap->data[child]))
    {
        Swap(&heap->data[parents],&heap->data[child]);
        AdjustUp(heap, parents);
    }else
    {
        return;
    }
}
//插入元素
void HeapInsert(Heap* heap, HeapType value)
{
    if(heap == NULL)
    {
        //非法输入
        return;
    }
    if(heap->size >= HeapMaxSize)
    {
        //堆满了
        return;
    }
    heap->data[heap->size++] = value;
    size_t child = heap->size - 1;
    AdjustUp(heap, child);
}
//下潜函数
void AdjustDown(Heap* heap, size_t parents, size_t size)
{                                                                                                                                       
    if(heap == NULL || parents >= size - 1 )
    {
        return;
    }
    size_t child = parents*2 + 1;//先定义其左孩子结点
    while(child > 0 && child < size)//如果左孩子结点存在,进入循环
    {
        //(以大堆为例)
        //我们是不清楚其左右孩子结点谁大谁小的,要确定出大的那个值才能进行交换
        if(child + 1 < size)//如果存在右孩子结点
        {
            //(以大堆为例)
            //如果右孩子结点的值大于左孩子结点,child+1就定位到右孩子结点了
            //否则就是左孩子结点
            if(!heap->com(heap->data[child],heap->data[child+1]))
            {
                child += 1;
            }
        }
        //如果父结点的值小于左右孩子结点中的最大值,就要进行交换
        //否则退出循环,表明已找到合适位置
        if(!heap->com(heap->data[parents],heap->data[child]))
        {
            Swap(&heap->data[parents],&heap->data[child]);
            parents = child;
            child = parents*2 + 1;
        }else{
            break;
        }
    }
    return;
}
//删除堆顶元素
void HeapErase(Heap* heap)
{
    if(heap == NULL)
    {
        //非法输入
        return;
    }
    if(heap->size <= 0)
    {
        //空堆
        return;
    }
    Swap(&heap->data[heap->size-1], &heap->data[0]);
    heap->size--;
    AdjustDown(heap,0,heap->size);
    return;
}
  • 取堆顶元素太简单了,就是返回数组下标为0的值,即根节点,代码就不粘了。
  • 创建一个堆,即给定一个无序数组,将其实现为一个堆,遍历数组,循环的进行插入操作就好了。介绍这个主要是为了堆排序
//创建堆
void HeapCreate(HeapType array[], Heap* heap,size_t size)
{
    if(heap == NULL || size <= 0)
    {
        return;
    }
    size_t index = 0;
    while(size--)
    {
        HeapInsert(heap,array[index]);
        index++;
    }
}
  • 堆排序
    • 给定一个无序数组,使用堆排序的方式将其有序化,很简单,我们先把该数组创建成一个堆,我们只能保证父结点的值大于孩子结点的值,不能保证整个数组有序。
    • 这时我们就要用到删除堆顶元素操作,每次删除的时候都是把堆顶元素交换到了数组末尾,有效数据-1,而并没有真正的将其移除,所以我们进行循环的删除操作,以大堆为例,每次被放置在数组末尾的元素都是堆中的最大值,所以当堆为空的时候,整个数组也就变成升序数组了(不考虑size)。最后将其memcpy到原来的数组中即可
    • 注意:升序数组用大堆,降序数组用小堆
//堆排序
void HeapSort(HeapType array[], Heap* heap, size_t size)
{
    if(heap == NULL || size <= 0)
    {
        return;
    }
    size_t num = size;
    HeapCreate(array,heap,size);
    while(size--)
    {
        HeapErase(heap);
    }
    memcpy(array,heap->data,num*sizeof(HeapType));
}

猜你喜欢

转载自blog.csdn.net/it_xiaoye/article/details/80328683