数据结构-实现堆的相关操作

堆的概念

首先:堆是一个完全二叉树。
堆有两种:大堆及小堆
小堆: 这个树的根节点是这个树中的最小的元素
对于任意一个子树来说, 子树的根节点, 小于左右孩子节点的值.
大堆: 这个树的根节点是这个树种的最大元素
对于任意一个子树来说, 子树的根节点, 大于左右孩子节点的值.
举例说明:

这里写图片描述

实现操作

  • 堆的初始化与销毁
  • 向堆中插入一个元素
  • 给一个数组创建一个堆
  • 取堆顶元素
  • 删除堆顶元素
  • 根据以上操作,实现一个堆排序

堆的定义结构如下:
将堆的层序遍历结果存储在一个数组中,可以很方便的通过下标的关系访问到某个元素。
函数指针定义了是大堆还是小堆的判断函数。

typedef struct Heap{
    HeapType data[HEAPMAX];
    size_t size;
    Compare cmp;
}Heap;

初始化与销毁
初始化与销毁主要是对堆结构体中size进行操作,只要size定义为0,那么这个数组中的元素的值全部变为了无效元素。

void HeapInit(Heap* heap, Compare cmp)
{
    if(heap==NULL)
    {
        return;
    }
    heap->cmp=cmp;
    heap->size=0;
}
void HeapDestroy(Heap* heap)
{
    if(heap==NULL)
    {
        return;
    }
    heap->size=0;
    heap->cmp=NULL;
}

向堆中插入一个元素
向堆中插入一个元素:首先是用数组保存该堆结构,所以插入操作就相当于尾插操作。但是必须要保证这个堆插入之后还要满足一个大堆或小堆的结构就要对该堆进行调整。对新插入的结点进行上浮式的调整。时间复杂度为:O(logN)

void Swap(HeapType* x,HeapType* y)
{
    HeapType tmp=*x;
    *x=*y;
    *y=tmp;
}
void HeapInsert(Heap* heap, HeapType to_insert)
{
    if(heap==NULL)
    {
        return;
    }

    if(heap->size>=HEAPMAX)
    {
        return;
    }
    heap->data[heap->size++]=to_insert;
    size_t child=heap->size-1;
    size_t parent=(child-1)/2;
    while(child>=0&&child<heap->size&&parent>=0&&parent<heap->size)
    {
        if(heap->data[child]>heap->data[parent])
        {
            Swap(&heap->data[child],&heap->data[parent]);
            child=parent;
            parent=(child-1)/2;
        }
        else
        {
            break;
        }
    }
}

根据一个数组,创建一个堆
对数组中的每一个元素都进行插入堆的操作,在insert函数内部会做调整。

void HeapCreat(Heap* heap, HeapType array[], size_t size)
{
    if(heap==NULL||array==NULL||size==0)
    {
        return;
    }
    size_t i=0;
    for(i=0;i<size;i++)
    {
        HeapInsert(heap,array[i]);
    }
}

取堆顶元素
由于,堆满足完全二叉树的性质。所以取堆顶元素就是取出根节点的元素,我们是可以直接访问到根节点的,就是数组中下标为0的元素。

int HeapRoot(Heap* heap, HeapType* root)
{
    if(heap==NULL)
    {
        return 0;
    }
    if(heap->size==0)
    {
        return 0;
    }
    *root=heap->data[0];
    return 1;
}

删除堆顶元素
对堆顶元素删除采用的思路是,先将下标为0的元素与最后一个元素交换,将堆顶元素换到数组中最后一个元素。
然后对数组进行尾删。就将堆顶元素删除了。
但是删除元素之后还要满足大堆或小堆的性质。
所以又要对堆进行下沉式的调整。

void HeapErase(Heap* heap)
{
    if(heap==NULL)
    {
        return;
    }
    if(heap->size==0)
    {
        return;
    }
    Swap(&heap->data[0],&heap->data[heap->size-1]);
    --heap->size;
    size_t parent=0;
    size_t child=parent*2+1;
    while(child>=0&&child<heap->size&&parent>=0&&parent<heap->size)
    {
        if(child+1<heap->size)//找到满足右子树的就要进行堆左右子树的大小判断,与更小的那个交换
        {
            if(!heap->cmp(heap->data[child],heap->data[child+1]))
            {
                child=child+1;
            }
        }
        if(heap->data[child]>heap->data[parent])
        {
            Swap(&heap->data[child],&heap->data[parent]);
            parent=child;
            child=parent*2+1;
        }
        else
        {
            break;
        }
    }
}

堆排序
我们在删除堆顶元素的时候发现,每删除一个元素之后,数组都将变得有序。
所以,先根据已给定要排序数组创建一个堆,在循环对这个堆进行删除。
最后将保存堆的数组拷贝至原数组中。

void HeapSort(HeapType array[], size_t size)
{
    if(array==NULL||size==0)
    {
        return;
    }
    Heap heap;
    HeapInit(&heap,Greater);
    HeapCreat(&heap,array,size);
    size_t i=0;
    for(i=0;i<size;i++)
    {
        HeapErase(&heap);
    }
    heap.size=size;
    for(i=0;i<size;i++)
    {
        array[i]=heap.data[i];
    }
}

猜你喜欢

转载自blog.csdn.net/mxrrr_sunshine/article/details/80312968