最大堆最小堆的实现(C语言)

堆是特殊的队列,从堆中取元素是按照元素的优先级大小,而不是元素进入队列的先后顺序。因此,堆也通常被称为“优先队列”。

堆的最常用结构是用二叉树表示,不特指的话,他是一棵完全二叉树。因此通常不必用指针,而是用数组来实现堆的存储。我们

知道,完全二叉树用数组来表示,就相当于把全完二叉树的层序遍历依次存入数组中,知道最后一个节点。

需要注意的是,所用的数组的起点为1,而不是0。这样的目的是很容易能够从父节点(i)找到子节点[ 2i ] [ 2i+1 ],反过来也很容易从子节点(j)找到父节点[ j/2 ]。 

堆的特性:

用数组来表示完全二叉树是堆的第一特性:堆的结构特性

任一节点的值与其子节点的值是相关的:部分有序性

相关性的不同决定了两种不同的基本堆:最大堆(MaxHeap)(任一节点大于等于其子节点)和最小堆(MinHeap)(任一节点小于等于其子节点)。注意,兄弟节点之间没有约束关系。

当我们需要小键值优先时,使用最小堆;需要大键值优先时,使用最大堆。

首先来看一下堆的一般结构体实现:

typedef struct HNode * heap;//结构体指针
struct HNode{
    ElementType *Data;//表示堆的数组 大小要在用户输入的元素个数上+1
    int Size;//数组里已有的元素(不包含a[0]) 
    int Capacity; //数组的数量上限
};
typedef heap MaxHeap; //定义一个最大堆
typedef heap MinHeap; //定义一个最小堆

创建:

//mheap表示maxHeap或者minHeap
mHeap creatMHeap(int size){
    mHeap heap = (mHeap)malloc(sizeof(struct Hnode));
    heap->data = (int*)malloc(sizeof(int)*(size+1));//从a[1]开始保存数 所以数组数量要+1
    heap->Size = 0;
    heap->Capacity=size;
    heap->data[0] = MAXData OR MINDATA;//岗哨 稍后会提到
    return heap;
}

接下来用代码来表示最大堆和最小堆的相关操作:

最大堆(MaxHeap):

插入:

从新增的最后一个节点的父节点开始,用要插入的元素向下过滤上层节点(比该元素小的下移)。

bool insertToHeap(maxHeap heap, int x){
    //查看是否已经满了
    if (heap->Size == heap->Capacity) {
        return false;
    }
    int i = ++heap->Size;//新增的节点位置
    for (; heap->data[i/2]x 循环就会停下来
        heap->data[i] = heap->data[i/2];
    }
    heap->data[i] = x;
    return true;
}

删除:

从根节点开始,用最大堆中的最后一个元素向上过滤下层节点(比该元素大的上移)。

int deleteHeap(maxHeap heap){
    //判断是否空
    if (heap->Size==0) {
        return false;
    }
    int top = heap->data[1];//堆顶元素(最大)
    int last = heap->Size--;//取出数组最后一个元素
    int parent = 1;
    int child;
    for (; parent*2<=heap->Size; parent=child) {
        child = 2*parent;//左子节点
        //查找是否有右子节点 并且 右子节点的元素大于左子节点
        if (child!=heap->Size && heap->data[child]data[child+1]) {
            child++;//右子节点
            if (lastdata[child]) {
                heap->data[parent] = heap->data[child];
            }else{//找到了位置
                break;
            }
        }
    }
    heap->data[parent] = last;
    return top;
}

构造:

虽说构造一个最大堆时只要把一个个元素按照插入方法插入到数组中即可完成。但是其时间复杂度是(O(N log N))。我们有一种更简单的方式,使得时间复杂度下降到O(N)。

1.将输入的元素按顺序放入完全二叉树(数组)中。

2.调整各个节点的位置,满足最大堆的有序性。

调整的过程就是从最后一个父节点开始倒序到根节点,逐一向下进行过滤操作(同删除的向下过滤一样,不过过滤元素就是父节点本身的元素)。

void percDown(maxHeap heap, int n){
    int top,parent,child;
    top = heap->data[n];//取出父节点元素
    //向下过滤
    for (parent = n; parent*2Size; parent=child) {
        child = 2*parent;
        if (child!=heap->Size && heap->data[child]data[child+1]) {
            child++;
            if (heap->data[child]<=n) {
                break;
            }else
                heap->data[child] = heap->data[parent];
        }
    }
    heap->data[parent] = top;
}

void buildMaxHeap(maxHeap heap){
    for (int i=heap->Size/2; i>0; i--) {//从最后一个父节点开始
        percDown(heap, i);
    }
}

最小堆(MaxHeap):

由于最大堆和最小堆只是元素的顺序位置不同,具体的操作只是细节判断的修改,就只提下插入和删除操作把:

插入:


bool insertMinHeap(minHeap heap, int x){
    //判断是否满了
    if (heap->Size == heap->Capacity){
        return false;
    }
    int p = ++heap->Size;
    for (; heap->data[p/2]data[p] = heap->data[p/2];
    }
    heap->data[p] = x;
    return true;
}

删除:

int deleteFromMinHeap(minHeap heap){
    int top = heap->data[1];
    int last = heap->data[heap->Size--];
    int parent,child;
    for (parent = 1; parent*2Size; parent=child) {
        child = parent*2;
            //注意这里是存在右子节点 并且 右子节点比左子节点小    
        if (child!=heap->Size && heap->data[child] > heap->data[child+1]) {
            child++;
            //如果比右子节点还小
            if (heap->data[child]>last) {
                break;
            }else{//下滤
                heap->data[parent] = heap->data[child];
            }
        }
    }
    heap->data[parent] = last;
    return top;
}

 

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