大根堆的插入、删除、修改优先级实现

       大根堆其实就是一个一维数组, 不过数组中数据的排列是基于完全二叉树模型的。根节点的数据大于等于左孩子和右孩子节点的数据,然后每个子树都和前面一样。它的主要应用是在优先级队列中, 因为大根堆或小根堆总是能很方便的找出最大的值或最小值,虽然有序的线性表一样可以实现该功能,但是有序线性表的致命缺陷在与入队时为了保持线性表仍是有序的,可能需要移动大量元素, 而大根堆和小根堆则相对有着更好的性能。

       用数组来存储大根堆时,可以从第1个元素开始存储,这样的好处是可以很方便的求出当前元素的父亲结点和孩子结点。 如当前元素下标是i,则他的左孩子为2*i, 右孩子为2I + 1,父亲结点为i/2。

//heap.h

#ifndef HEAP_H_
#define HEAP_H_
#include
#define MAX_ELEMENTS 200


typedef struct Element{
    int key;
    /*other field*/
}Element;


typedef struct Heap{
    Element heap[MAX_ELEMENTS];
    int n;
}Heap;


void initHeap(Heap *php);
void push(Heap * php, Element e);//插入大根堆
Element pop(Heap *php);//删除根结点
void showHeap(Heap * php);
void setvalue(Heap * php, int index, int value);// 修改第index个元素的key为value


#endif


//heap.c

#include "heap.h"
#include


void push(Heap * php, Element e)
{
    int i;
    Element temp;
    if(!php)
        return;
    if(php->n == MAX_ELEMENTS)
    {
        fprintf(stderr, "heap full.\n");
        exit(EXIT_FAILURE);
    }
    i = ++(php->n);
    php->heap[i] = e;
    while(i != 1 && e.key > php->heap[i/2].key)
    {
        temp = php->heap[i/2];
        php->heap[i/2] = php->heap[i];
        php->heap[i] = temp;
        i /= 2;
    }
}

Element pop(Heap *php)
{
    int i;
    Element e, item;
    int child;
    
    i = 1;
    child = 2;
    if(php->n == 0)
    {
        fprintf(stderr, "heap empty.\n");
        exit(EXIT_FAILURE);
    }
    e = php->heap[i];
    item = php->heap[(php->n)--];
    while(child <= php->n)
    {
        if(child + 1 <= php->n && php->heap[child+1].key > php->heap[child].key)
            child++;
        if(item.key > php->heap[child].key)
            break;
        php->heap[i] = php->heap[child];
        i = child;
        child = 2 * i;
    }
    php->heap[i] = item;
    
    return e;
}

void initHeap(Heap *php)
{
    if(!php)
        return;
    php->n = 0;
}

void showHeap(Heap * php)
{
    int i;
    if(NULL == php)
        return;
    
    for(i = 1; i <= php->n; i++)
    {
        printf("%d\t", php->heap[i].key);
        if(i % 5 == 0)
            printf("\n");
    }
    printf("\n");
}

void setvalue(Heap * php, int index, int value)
{
    Element temp;
    int oldkey;
    
    if(!php)
        return;
    
    oldkey = php->heap[index].key;
    php->heap[index].key = value;
    temp = php->heap[index];
    
    if(value > oldkey)//增大
    {
        while (index != 1 && value > php->heap[index/2].key)
        {
            php->heap[index] = php->heap[index/2];
            index /= 2;
        }
    }
    else
    {
        int child;
        child = 2 *index;
        while(child <= php->n)
        {
            if(child < php->n && php->heap[child+1].key > php->heap[child].key)
                child++;
            if(temp.key >= php->heap[child].key)
                break;
            php->heap[index] = php->heap[child];
            index = child;
            child = child * 2;
        }        
    }
    php->heap[index]= temp;
}


//main.c


#include "heap.h"
#include
#include


int main(void)
{
    Heap hp;
    int i;
    srand(time(NULL));
    Element e;
    
    initHeap(&hp);
    for(i = 0; i < 10; i++)
    {
        e.key = rand()%100;
        push(&hp, e);
    }
    printf("show heap: \n");
    showHeap(&hp);

    printf("set 1 to 0: \n");
    setvalue(&hp, 1, 0);
    showHeap(&hp);

    for(i = 1; i <= 10; i++)
    {
        e = pop(&hp);
        printf("pop %d: \n", e.key);
        showHeap(&hp);
    }
    
    return 0;
}

这个程序的POP函数和设置优先级函数较难实现,想了很久,思维也很混乱,后来还是得参考书上的思路才比较明了。主要问题是在向下遍历寻找最大结点时,需要分成左右子树,而且子树的下标索引有可能会超出堆中元素总数。当碰到这种感觉需要用很多if else才能完成的程序时,该考虑考虑程序的设计了。上面使用child变量来表示2个子结点,先将child确定下来,然后统一操作,思路要清晰很多。


你可能感兴趣的:(数据结构和算法)