大根堆的实现

堆结构介绍:
  • 大根堆:根结点的值比它的左右子树都要大,同时它的左右子树也遵循这项规则,也就是说一棵树的根结点中存储的是这棵树中的最大值,这种完全二叉树叫大根堆。

  • 小根堆:根结点的值比它的左右子树都要小,同时它的左右子树也遵循这项规则,也就是说一棵树的根结点中存储的是这棵树中的最小值,这种完全二叉树叫小根堆。

  • 堆结构是是一种特殊的完全二叉树,它与堆栈完全不同且没有任何关系,我们可以从堆中顺序获取堆顶的数据,也可以使用大根堆、小根堆的规则可以对顺序表进行排序。

#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define PARENT(index)       ((index+1)/2-1)
#define LEFT_CHILD(index)   ((index+1)*2-1) 
#define RIGHT_CHILD(index)  ((index+1)*2)

#define swap_mem(p1,p2,size) do{    \
    char temp[size];                \
    memcpy(temp,p1,size);           \
    memcpy(p1,p2,size);             \
    memcpy(p2,temp,size);           \
}while(0)

#define swap(a,b) swap_mem(&a,&b,sizeof(a))


typedef struct MaxHeap
{
    int* base;
    size_t cnt;
    size_t cal;
}MaxHeap;

// 把base中的数据调整为堆结构
void adjust_max_heap(MaxHeap* heap,int index)
{
    // 从index下标处,向上调整堆结构
    while(index > 0)
    {
        // 计算出双亲下标
        int parent = PARENT(index);
        // 计算出双亲结点的左子树
        int left = LEFT_CHILD(parent);
        // 如果左子树下标合法并且值大于双亲,则与双亲交换
        if(left < heap->cnt && heap->base[left] > heap->base[parent])
            swap(heap->base[left],heap->base[parent]);

        int right = RIGHT_CHILD(parent);
        // 如果右子树下标合法并且值大于双亲,则与双亲交换
        if(right < heap->cnt && heap->base[right] > heap->base[parent])
            swap(heap->base[right],heap->base[parent]);
    
        // 向上一层
        index = parent;
    }

}

// 创建一个空堆
MaxHeap* create_max_heap(size_t cal)
{
    MaxHeap* heap = malloc(sizeof(MaxHeap));
    heap->base = malloc(sizeof(int)*cal);
    heap->cal = cal;
    heap->cnt = 0;
    return heap;
}

// 初始化堆
MaxHeap* init_max_heap(int* arr,size_t len)
{
    MaxHeap* heap = malloc(sizeof(MaxHeap));
    heap->base = malloc(sizeof(int)*len);
    memcpy(heap->base,arr,sizeof(int)*len);
    heap->cal = len;
    heap->cnt = len;

    // 从叶子结点出发向上调整,最后一个叶子结点的双亲结点就是最后一个非叶子结点
    for(int i=len-1; i>len/2; i--)
        adjust_max_heap(heap,i);
    
    return heap;

}


// 销毁堆
void destroy_max_heap(MaxHeap* heap)
{
    free(heap->base);
    free(heap);
}

// 堆是否为空
bool empty_max_heap(MaxHeap* heap)
{
    return 0 == heap->cnt;
}

// 堆是否为满
bool full_max_heap(MaxHeap* heap)
{
    return heap->cal == heap->cnt;
}

// 入堆
bool push_max_heap(MaxHeap* heap,int val)
{
    if(full_max_heap(heap))
        return false;
    

    // 在末尾添加一个新结点
    heap->base[heap->cnt++] = val;
    // 从最后一个结点出发向上调整
    adjust_max_heap(heap,heap->cnt-1);

}

// 出堆
bool pop_max_heap(MaxHeap* heap)
{
    if(empty_max_heap(heap))
        return false;

    // 使用最后一个结点覆盖根结点,并且结点的数量减1
    heap->base[0] = heap->base[--heap->cnt];
    
    // 从根结点出发向下调整
    int parent = 0;
    while(parent < heap->cnt)
    {
        // 假定根结点是根、左右中的最大结点,并计算出它的左右子树的下标
        int max=parent, left=LEFT_CHILD(parent),right=RIGHT_CHILD(parent);
        
        // 如果左子树下标合法,且左子树的值大于最大结点,则把左子记作最大结点
        if(left < heap->cnt && heap->base[max] < heap->base[left])
            max = left;
        // 如果右子树下标合法,且右子树的值大于最大结点,则把右子记作最大结点
        if(right < heap->cnt && heap->base[max] < heap->base[right])
            max = right;
    
        // 如果最大结点就是根结点,则不需要再向下调整,
        if(max == parent)
            break;
    
        // 把最大结点与根结点交换,从从大结点处继续向下调整
        swap(heap->base[max],heap->base[parent]);
        parent = max;
    }
    return true;

}

// 堆顶
int top_max_heap(MaxHeap* heap)
{
    if(empty_max_heap(heap))
        return -1;
    return heap->base[0];
}

// 元素数量
size_t size_max_heap(MaxHeap* heap)
{
    return heap->cnt;
}   

int main(int argc,const char* argv[])
{
    MaxHeap* heap = create_max_heap(10);
    int arr[10];
    for(int i=0; i<10; i++)
    {
        arr[i] = rand() % 100;
        printf("%d ",arr[i]);
        push_max_heap(heap,arr[i]);
    }
    printf("\n");
    while(!empty_max_heap(heap))
    {
        printf("%d ",top_max_heap(heap));
        pop_max_heap(heap);
    }
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容