算法入门:优先队列实现——堆

定义

优先队列(PriorityQueue),根据key值的大小将元素进行排序、先被pop的通常是优先级最高的。此处介绍基于堆实现的优先队列,binary heap是一种完全二叉树,以大堆为例,每棵树的根节点的key值一定大于其子孙节点的key值,完全二叉树除了最底层的叶子节点外,其他位置都是填满的。这样我们可以利用数组来存储所有节点。

《算法入门:优先队列实现——堆》

若将数组从下标1开始存储元素、那么下标为 i 的节点的左孩子节点的下标为 2 * i、而右孩子节点下标为 2 * i + 1;这样可以很容易定位一个节点的孩子节点或父节点的在数组中位置。

 

《算法入门:优先队列实现——堆》

插入数据

  1. 添加元素,也就是需要将元素添加到最底层叶子节点从左向右的空位置上。就是vector的最后一个位置。
  2. 通过下标计算出新节点的父亲节点、与父节点key值比较、若比父节点大、则与父节点进行位置交换、不断上溯直到比父节点小或者父节点为空(也就是本身成为了根节点)。
  3. 在与父节点交换时、只需要交换他们的值就好。
  • 插入一个111,首先把111 插入到底部

《算法入门:优先队列实现——堆》

  • 调整堆,把111 和 111的顶部parentNode进行比较,发现111大于33,所有需要改变exchange 111 和 33的位置

《算法入门:优先队列实现——堆》

  • 接着111 和 parentNode比,发现还是大于ParentNode,所以继续改变位置。

《算法入门:优先队列实现——堆》

  • 继续比较

《算法入门:优先队列实现——堆》

  • 到达top了,没有元素比111大了。此时堆变得有序了。

删除数据

  1. 因为是队列、每次pop是删除都是删除根节点。
  2. 由于要保证heap的complete binary tree结构、每次删除的节点实际上都是最后一块内存空间(vector中最后一个位置)。
  3. 删除根节点时需要比较左右节点的key值、与删除节点进行交换,然后以删除节点位置开始向下查找(重复)。
  • 首先我们先把111和 最底部的元素交换位置,并且取出111。

​​​​​​​《算法入门:优先队列实现——堆》

  • 把top部的元素和2个子元素比较大小,如果比子元素小的话,需要交换他们的位置,把33 和 Math.max(100, 50)交换位置。

​​​​​​​《算法入门:优先队列实现——堆》

  • 33 比 90 小,交换他们的位置

《算法入门:优先队列实现——堆》

#include <iostream>
#include <vector>
using namespace std;

template<typename T>
class PriorityQueue {
    private:
        // 在heap内部维护一个数组vector用来保存元素、方便建立堆和节点的添加删除
        vector<T> v;
        int size;
        const static int initCap = 100;
    private:
        int parent(int pos) {
            return pos/2;
        }

        int left(int pos) {
            return pos*2;
        }

        int right(int pos) {
            return pos*2 + 1;
        }
    public:
        PriorityQueue():
            size(0) {
                v = vector<T>(initCap);
            }

        bool enQueue(const T& t) {
            size++;
            v[size] = t;
            int i = size;
            //插入到尾部,一直上提到使当前堆符合最大堆性质为止
            while(i > 1 && v[i] > v[parent(i)]) {
                T tmp = v[i];
                v[i] = v[parent(i)];
                v[parent(i)] = tmp;
                i = parent(i);
            }
            return true;
        }

        bool deQueue(T& max) {
            //取出最大值,并把尾部元素和最大值交换,然后维护当前堆到符合堆性质
            if(size < 1) {
                return false;
            }
            max = v[1];
            v[1] = v[size];
            v[size] = max;
            size--;
            maxHeapIfy(1);
            return true;
        }

        void maxHeapIfy(int pos) {
            int largestPos = pos;
            if(left(pos) <= size && v[pos] < v[left(pos)]) {
                largestPos = left(pos);
            }
            if(right(pos) <= size && v[largestPos] < v[right(pos)]) {
                largestPos = right(pos);
            }
            if(largestPos != pos) {
                T tmp = v[pos];
                v[pos] = v[largestPos];
                v[largestPos] = tmp;
                maxHeapIfy(largestPos);
            }
        }

        int getSize() {
            return size;
        }

        bool top(T& t) {
            if(size < 1) {
                return false;
            }
            t = v[1];
            return true;
        }
};

int main() {
    PriorityQueue<int> pQ;
    pQ.enQueue(8);
    pQ.enQueue(1);
    pQ.enQueue(3);
    pQ.enQueue(6);
    pQ.enQueue(5);
    pQ.enQueue(3);
    pQ.enQueue(10);
    pQ.enQueue(9);
    pQ.enQueue(7);
    int a = -1;
    while(pQ.deQueue(a)) {
        cout<<a<<" ";
    }
    cout<<endl;
    return 0;
}

 

点赞