数据结构基础(19) --堆与堆排序

完全二叉树

 

首先让我们回顾一下完全二叉树的两个性质:

  性质1:具有n个结点的完全二叉树的深度为[logn](向下取整)+1。

  性质2:若对含 个结点的完全二叉树从上到下且从左至右进行 至 的编号,则对完全二叉树中任意一个编号为 的结点:

    (1) 若 i=1,则该结点是二叉树的根,无双亲,否则,编号为 [i/2](向下取整)的结点为其双亲结点;
    (2) 若 2i>n,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点;
    (3) 若 2i+1>n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。

 

数组与完全二叉树

 

从上图可以看出, 如果完全二叉树从上到下且从左至右进行从0至n-1进行编号,则对完全二叉树的性质需要修改如下:

   (1) 若 i=0,则该结点是二叉树的根,无双亲,否则,编号为 [(i-1)/2](向下取整)的结点为其双亲结点;
   (2) 若 2i+1>n,则该结点无左孩子,否则,编号为 2i+1 的结点为其左孩子结点;
   (3) 若 2i+2>n,则该结点无右孩子结点,否则,编号为2i+2 的结点为其右孩子结点。


大顶堆与小顶堆

 

堆是满足下列性质的数列{r1, r2, …,rn}:


(小顶堆)


(大顶堆)

 

大顶堆的设计

template <typename Type>
class MaxHeap
{
public:
    MaxHeap(int _maxSize = 10);
    virtual ~MaxHeap();

    bool isEmpty() const;
    void push(const Type &item);
    void pop();
    const Type &top() const;

private:
    //将堆的容量增大两倍
    void resize();
    //向上渗透
    void trickUp(int index);
    //向下渗透
    void trickDown(int index);

private:
    Type *heapArray;
    int maxSize;
    //currentSize有两个含义:
    // 1.代表当前堆中已经存储的元素数
    // 2.代表当前完全二叉树的第一个空位
    int currentSize;
};

大顶堆的实现

//构造
template <typename Type>
MaxHeap<Type>::MaxHeap(int _maxSize)
    : maxSize(_maxSize),currentSize(0)
{
    if (maxSize < 1)
        throw std::length_error("heap size must >= 1");

    heapArray = new Type[maxSize];
}
//析构
template <typename Type>
MaxHeap<Type>::~MaxHeap()
{
    delete []heapArray;
    heapArray = NULL;
    currentSize = 0;
}
//判空
template <typename Type>
bool MaxHeap<Type>::isEmpty() const
{
    return currentSize == 0;
}

堆顶元素

//查看堆顶元素
template <typename Type>
const Type &MaxHeap<Type>::top() const
{
    if (isEmpty())
        throw std::underflow_error("heap is empty");

    return heapArray[0];
}

插入元素

//插入
template <typename Type>
void MaxHeap<Type>::push(const Type &item)
{
    //如果堆已满, 则需要对堆进行扩充
    if (currentSize == maxSize)
    {
        resize();
    }
    //将元素插入到堆的第一个空位置上
    heapArray[currentSize] = item;

    //维护堆的性质:向上渗透
    trickUp(currentSize);
    ++ currentSize;
}
//向上渗透, 将刚刚插入的元素移动到合适的位置上
template <typename Type>
void MaxHeap<Type>::trickUp(int index)
{
    //根据完全二叉树的性质, 找到父节点
    int parent = (index-1)/2;
    Type bottom = heapArray[index];

    //循环终止条件
    // 1. index = 0 //bottom元素插入到根节点
    // 2. heapArray[parent] >= bottom   //bottom插入到parent节点的一个子节点上
    while ((index > 0) && (heapArray[parent] < bottom))
    {
        //父节点下移
        heapArray[index] = heapArray[parent];
        index = parent;
        parent = (parent-1)/2;
    }
    //插入
    heapArray[index] = bottom;
}
//将堆的大小加倍
template <typename Type>
void MaxHeap<Type>::resize()
{
    int newSize = std::max(maxSize*2, 1);
    Type *newHeap = new Type[newSize];
    for (int i = 0; i < currentSize; ++i)
    {
        newHeap[i] = heapArray[i];
    }

    delete []heapArray;
    heapArray = newHeap;
    maxSize = newSize;
}

删除堆顶元素

//删除
template <typename Type>
void MaxHeap<Type>::pop()
{
    if (isEmpty())
        throw std::underflow_error("heap is empty");

    //只有一个元素
    if (currentSize == 1)
    {
        heapArray[0].~Type();
        currentSize = 0;
        return ;
    }

    //显示释放堆顶元素
    heapArray[0].~Type();
    //直接将最有一个元素放到堆顶,
    //并且currentSize-1
    heapArray[0] = heapArray[-- currentSize];

    //此时如果破坏了堆的性质:向下渗透
    trickDown(0);
}
//向下渗透
template <typename Type>
void MaxHeap<Type>::trickDown(int index)
{
    int largeChild;
    Type top = heapArray[index];

    // 只需到达完全二叉树的最后一层
    // 的上一层即可
    // 循环的终止条件:
    // 1. index已经到达了最后一层(index >= currentSize/2 :找到了一个比较合适的位置)
    // 2. 在堆的中间找到了一个合适的位置(top >= heapArray[largeChild])
    while (index < currentSize/2)
    {
        int leftChild = (index*2)+1;
        int rightChild = leftChild + 1;

        //如果有右儿子节点, 而且右儿子节点还大于左儿子节点
        if (rightChild < currentSize && heapArray[rightChild] > heapArray[leftChild])
            largeChild = rightChild;
        else
            largeChild = leftChild;

        if (top >= heapArray[largeChild])
            break;

        //不然的话, 则需继续向下渗透
        heapArray[index] = heapArray[largeChild];
        // index需要向下搜索
        index = largeChild;
    }
    //插入
    heapArray[index] = top;
}

堆排序

  堆排序就是将元素一个一个插入到堆中, 然后再一个一个的取出来;

//堆排序
template <typename Type>
void heapSort(Type *begin, Type *end)
{
    int size = end-begin;
    MaxHeap<Type> maxHeap(size);
    //存入堆中
    for (Type *current = begin; current < end; ++ current)
        maxHeap.push(*current);

    //从堆中取出
    while (begin != end)
    {
        *begin = maxHeap.top();
        ++ begin;
        maxHeap.pop();
    }
}

template <typename Type>
void heapSort(Type *array, int arraySize)
{
    return heapSort(array, array+arraySize);
}

-测试代码:

int main()
{
    int array[20];
    for (int i = 0; i < 20; ++i)
    {
        array[i] = rand()%100;
        cout << array[i] << ' ';
    }

    heapSort(array, 20);

    cout << endl;
    for (int i = 0; i < 20; ++i)
    {
        cout << array[i] << ' ';
    }
    cout << endl;

    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值