堆相关问题

讲堆不得不提的就是优先队列。

什么是优先队列?

普通队列:先进先出,后进后出

优先队列:出队顺序和入队顺序无关;和优先级相关

如何实现优先队列?

由上图可知,用堆来实现优先队列是一个理想的方法。

这里采用二叉堆(最大堆)的定义:

  1.   所有节点的值都不大于其父节点的值
  2.   二叉堆是一个完全二叉树
采用数组来存储二叉堆

由二叉堆的定义可以得出结论:对一颗完全二叉树按层序遍历编号,根节点的编号为1, 假设父节点的编号为 i , 则任何子节点的编号满足:

  • left child = i * 2
  • right child = i * 2 + 1

用C++语言一步一步实现最大堆

初始化:

#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
using namespace std;

//MaxHeap implements
template <typename Item>
class MaxHeap{

private:
    Item* data;
    int count;//堆的大小
    int capacity;
public:
    MaxHeap(int capacity) {
        data = new Item[capacity + 1];
        count = 0;
        this->capacity = capacity;
    }

    ~MaxHeap() {
        //释放内存空间
        delete [] data;
    }

    //return the size of maxheap
    int size() {
        return count;
    }

    int isEmpty() {
        return count == 0;
    }
    void print() {
        for(int i = 0; i <= this->count; i++) {
            cout << data[i] << "  ";
        }
        cout << endl;
    }
};
int main() {
    MaxHeap<int> maxheap = MaxHeap<int>(100);
    cout << maxheap.size() << endl;
    srand(time(NULL));
    return 0;
}
插入一个值:

首先将插入值放在数组的最后面,然后将值与父节点比较,如果父节点的值比插入值大,则当前位置就是合适位置;如果父节点的值比插入值小,则当前位置不合适,将当前值与父节点值交换,然后再向上比较。直到父节点的值比插入值大或者已到根节点。

void shiftUp(int k) {
    while(k > 1 && data[k/2] < data[k]) {
        swap(data[k / 2], data[k]);
        k /= 2;
    }
}
//插入一个值
void insert(Item item) {
    assert(count + 1 <= capacity );
    data[count + 1] = item;
    count++;
    shiftUp(count);
}
推出一个值:

    在最大堆中推出一个值,即最大值,也就是二叉树的根节点。

    可以先将数组中的最后一个元素赋值给第一个元素,由于我们堆有一个对的大小属性维护,不用删除最后一个元素的值。

    然后由根节点一次向下比较左右两个字节点的大小,如果左字节点小于右字节点,那么将右子节点与父节点交换,否则与左字节点交换。

void shiftDown(int k) {

    while( 2 * k <= count) {

        int j = 2 * k;//在次轮循环中,data[k]和data[j]交换位置
        if( j + 1 <= count && data[j + 1] > data[j])
            j++;

        if(data[k] >= data[j])
            break;
        swap(data[j], data[k]);
        k = j;
    }
}
Item extractMax() {
    assert(count > 0);
    Item ret = data[1];
    swap(data[1], data[count]);
    count--;
    shiftDown(1);
    return ret;
}
索引堆

当我们要建立堆的数据结构过于复杂时,交换元素困难,我们可以考虑使用索引堆,即采用一个indexes数组存储二叉最大堆的索引值,data数组存储值,每次只交换索引,而不交换值。这里给出一种实现方式。

template <typename Item>
class IndexMaxHeap{

private:
    int *indexes;
    Item* data;
    int count;//堆的大小
    int capacity;
    void shiftUp(int k) {
        while(k > 1 && data[indexes[k/2]] < data[indexes[k]]) {
            swap(indexes[k / 2], indexes[k]);
            k /= 2;
        }
    }

    void shiftDown(int k) {

        while( 2 * k <= count) {

            int j = 2 * k;//在次轮循环中,data[k]和data[j]交换位置
            if( j + 1 <= count && data[indexes[j + 1]] > data[indexes[j]])
                j++;

            if(data[indexes[k]] >= data[indexes[j]])
                break;
            swap(indexes[j], indexes[k]);
            k = j;
        }
    }

public:
    IndexMaxHeap(int capacity) {
        data = new Item[capacity + 1];
        indexes = new int[capacity + 1];
        count = 0;
        this->capacity = capacity;
    }

    ~IndexMaxHeap() {
        //释放内存空间
        delete [] data;
        delete [] indexes;
    }

    //return the size of maxheap
    int size() {
        return count;
    }

    int isEmpty() {
        return count == 0;
    }

    //插入一个值
    //用户以为i通常以为是从0开始索引的
    void insert(int i, Item item) {
        assert(count + 1 <= capacity );
        assert(i + 1 >= 1 && i + 1 <= capacity);
        i++;
        data[i] = item;
        indexes[count + 1] = i;
        count++;
        shiftUp(count);
    }

    void print() {
        for(int i = 0; i <= this->count; i++) {
            cout << data[i] << "  ";
        }
        cout << endl;
    }

    //返回最大的元素
    Item extractMax() {

        assert(count > 0);

        Item ret = data[indexes[1]];
        swap(indexes[1], indexes[count]);
        count--;
        shiftDown(1);
        return ret;
    }

    //返回最大元素的索引
    int extractMaxIndex() {

        assert(count > 0);

        int ret = indexes[1] - 1;
        swap(indexes[1], indexes[count]);
        count--;
        shiftDown(1);
        return ret;
    }

    //由索引来取值
    Item getItem(int i) {
        return data[i + 1];
    }

    void change(int i, Item newItem) {

        i++;
        data[i] = newItem;
        // 找到indexes[j] = i, j表示data[i]在堆中的位置
        //时间复杂度 n + logn, 即O(n)
        for(int j = 0; j <= count; j++)
            if(indexes[j] == i) {
                data[i] = newItem;
                shiftDown(j);
                shiftUp(j);
            }
    }
};






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值