堆排序及最大优先队列(C++实现)

堆在本质实现上是一个数组,所有对堆的操作都是通过数组下标来实现的。但抽象来看,它可以被看成一颗近似的完全二叉树,树上的每一个节点对应数组中的一个元素。

堆分为最大堆和最小堆,它们对树上的节点提出了要求:最大堆要求父节点的元素大小大于等于子节点的大小,而最小堆则相反。上述数组经过最大堆堆化后呈现如下的树状结构:

为了访问父节点,和左右子节点,我们需要定义操作PARENT(i),LEFT(i),RIGHT(i)来根据二叉树的性质得到该节点的访问下标。在大多数计算机上,通过将i的值左移一位,LEFT过程可以在一条指令内计算得出2i。采用类似方法,在RIGHT过程也可以通过将i的值左移一位并在低位加1,快速计算2i+1。PARENT过程,则可以通过把i值右移一位的得到\left \lfloor i/2 \right \rfloor,可以通过宏定义实现这个过程。但值得注意的是,要满足定义的操作,数组在逻辑上是从下标1开始,编程过程中仍从下标0开始。

要实现堆排序,堆类定义了以下几个成员函数

  • MAX-HEAPIFY过程:其时间复杂度位O(lgn),它是维护最大堆性质的关键。
  • BUILD-MAX-HEAP过程:具有线性时间复杂度,功能是从无序的输入数组中构造一个最大堆。
  • HEAPSORT过程:其时间复杂度为O(nlgn),功能是堆一个数组进行原址排序。
  • MAX-HEAP-INSERT、HEAP-EXTRACT-MAX、HEAP-INCREASE-KEY和HEAP-MAXIMUM过程:时间复杂度为O(lgn),功能是利用一个堆实现一个优先队列。

C++实现:

头文件Heap.h

#ifndef __Heap_H
#define __Heap_H

#include <iostream>
#include <math.h>
using namespace std;

#define LEFT(i) i << 1
#define RIGHT(i) (i << 1) + 1
#define PARENT(i) i >> 1
#define PASSIVE_INFINITE -100000;
#define EXCHANGE(arr,a,b) \
    {                     \
        int temp = arr[a];    \
        arr[a] = arr[b];    \
        arr[b] = temp;        \
    }

class Heap
{
public:
    struct Data
    {
        int size;
        int heap_size;
        int *heap;
    };

    Heap(int *arr, int size)
    {
        data.heap = (int *)malloc(sizeof(int) * (size));//分配动态内存
        for (int i = 0; i < size; i++)
            data.heap[i] = *(arr + i);
        data.size = size-1;//数组下标从0开始
    }
    void max_heapify(Data &d, int i);
    void build_max_heap(Data &d);
    void heap_sort(Data &d);
    int heap_maximum(Data &d) { return d.heap[d.heap_size]; };
    int heap_extract_max(Data &d);
    void heap_increase_key(Data &d, int i, int key);
    void max_heap_insert(Data &d, int key);
    Data data;
};

void Heap::max_heapify(Data &d, int i)
{
    int l = LEFT(i);
    int r = RIGHT(i);
    int largest;
    if (l <= d.heap_size && d.heap[l] > d.heap[i])
        largest = l;
    else
        largest = i;

    if (r <= d.heap_size && d.heap[r] > d.heap[largest])
        largest = r;

    if (largest != i)
    {

        EXCHANGE(d.heap, i, largest);
        max_heapify(d, largest);
    }
}

void Heap::build_max_heap(Data &d)
{
    d.heap_size = d.size;
    for (int i = floor(d.size >> 1); i >= 0; i--)
        max_heapify(d, i);
}

void Heap::heap_sort(Data &d)
{
    build_max_heap(d);
    for (int i = d.size; i >= 1; i--)
    {
        EXCHANGE(d.heap, 0, i);
        d.heap_size = d.heap_size - 1;
        max_heapify(d, 0);
    }
}
/*利用堆构造优先队列定义的操作*/
int Heap::heap_extract_max(Data &d)
{
    if (d.heap_size < 1)
        throw "heap underflow";
    int max = d.heap[d.heap_size];
    d.heap[d.heap_size] = d.heap[0];
    d.heap_size--;
    max_heapify(d, d.heap_size);
    return max;
}
void Heap::heap_increase_key(Data &d, int i, int key)
{
    if(key<d.heap[i])
        throw "new key is smaller than current key";
    d.heap[i]=key;

    while(i>1 && d.heap[PARENT(i)]<d.heap[i])
    {
        EXCHANGE(d.heap,i,PARENT(i));
        i=PARENT(i);
    }
}

void Heap::max_heap_insert(Data &d,int key)
{
    d.heap_size++;
    d.heap[d.heap_size]=PASSIVE_INFINITE;
    heap_increase_key(d, d.heap_size, key);
}
#endif

Heap.cpp:

#include<iostream>
#include<stdlib.h>
#include<time.h>
#include"Heap.h"

using namespace std;
#define max 50
#define min -50

int* getRandData(int n) {
    int *arr=(int *)malloc(sizeof(int) * n);
    for (int i = 0; i < n; i++)
        *(arr+i)=((rand() %(max-min+1))+min);
    return arr;
}
int main()
{
    srand(time(NULL));
    int size=20;
    int *arr=getRandData(size);//生成元素值-50~50,size大小的随机数数组
    for (int i=0;i<size;i++)
        cout<<*(arr+i)<<" ";
    cout<<endl;
    cout << "After sorted:" << endl;
    Heap heap(arr,size);
    heap.heap_sort(heap.data);
    for (int i=0;i<size;i++)
        cout << *(heap.data.heap + i) << " ";
    system("pause");
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值