堆Heap

2018-04-10  本文已影响0人  sunblog

Heap

Heap: 堆。一般也称作Priority Queue(即优先队列)

堆我们一般用一个二叉树来表示。即一个非叶子节点有至多2个子节点。

堆一般分为大堆和小堆。大堆的意思是,对任意一个非叶子节点,它的值大于它的子节点的值。此时,root(堆顶)的值是整棵树最大的值。小堆的意思刚好想法,对任意一个非叶子节点,它的值小于它的子节点的值。此时root的值是整棵树的最小值。

堆可以用来排序,也可以按优先级来存储数据。

c++中有std::priority_queue

下面是我自己的一个简单实现:

// .h
#ifndef AGORITHM_PRIORITYQUEUE_H
#define AGORITHM_PRIORITYQUEUE_H


#include <cstdint>
#include <vector>
#include <functional>

class PriorityQueue {
public:
    using ValueType = int;
    using CmpFunc = std::function<bool(ValueType, ValueType)>;

    virtual ~PriorityQueue() = default;

    explicit PriorityQueue();

    explicit PriorityQueue(uint32_t initialCap);

    explicit PriorityQueue(uint32_t initialCap, const CmpFunc &func);

    virtual void Push(int value);

    virtual int Pop();

    virtual int Top() const;

    bool Empty() const;

    uint32_t Size() const;

    bool IsLeaf(uint32_t pos);

    int DeleteNodeAt(uint32_t pos);

    ValueType ValueAt(uint32_t pos);

    static const int INITIAL_CAP = 8;

    static const int FIRST_POS = 1;

protected:
    CmpFunc GetCmpFunc() const;

    static uint32_t bubbleDown(std::vector<int> &heap, uint32_t heapSize, uint32_t position, const CmpFunc &cmpFunc);

    static int bubbleUp(std::vector<int> &heap, uint32_t root, uint32_t heapSize, uint32_t position,
                        const CmpFunc &cmpFunc);

private:
    std::vector<int> mHeap;
    CmpFunc mCmpFunc = nullptr;
    uint32_t mHeapSize = 0;
};

#endif //AGORITHM_PRIORITYQUEUE_H


// .cpp


PriorityQueue::PriorityQueue() : PriorityQueue(INITIAL_CAP, nullptr) {
}

PriorityQueue::PriorityQueue(uint32_t initialCap) : PriorityQueue(initialCap, nullptr) {
}

PriorityQueue::PriorityQueue(uint32_t initialCap, const CmpFunc &func)
        : mHeap(initialCap), mCmpFunc(func) {
    if (!mCmpFunc) {
        mCmpFunc = [](ValueType v1, ValueType v2) -> bool {
            return v1 <= v2;
        };
    }
}

void PriorityQueue::Push(int value) {
    if (mHeapSize + 1 >= mHeap.size()) {   // enlarge heap
        mHeap.resize(mHeapSize * 2);
    }
    mHeap[++mHeapSize] = value;
    bubbleUp(mHeap, FIRST_POS, mHeapSize, mHeapSize, mCmpFunc);
}

int PriorityQueue::Pop() {
    if (Empty()) {
        throw std::out_of_range("queue empty");
    }

    int value = mHeap[FIRST_POS];

    std::swap(mHeap[FIRST_POS], mHeap[mHeapSize--]);
    bubbleDown(mHeap, mHeapSize, FIRST_POS, mCmpFunc);
    return value;
}

int PriorityQueue::Top() const {
    return mHeap[FIRST_POS];
}

bool PriorityQueue::Empty() const {
    return mHeapSize == 0;
}

uint32_t PriorityQueue::bubbleDown(std::vector<int> &heap, uint32_t heapSize, uint32_t position,
                                   const CmpFunc &cmpFunc) {
    if (heapSize <= 1) {
        return heapSize;
    }

    int value = heap[position];

    uint32_t j = position;

    while (j * 2 <= heapSize) {
        uint32_t child = j * 2;
        if (child + 1 <= heapSize && cmpFunc(heap[child + 1], heap[child])) {
            child++;
        }
        if (!cmpFunc(value, heap[child])) { // value > head[child]
            std::swap(heap[j], heap[child]);
            j = child;
        } else {
            break;
        }
    }
    heap[j] = value;
    return j;
}

int PriorityQueue::bubbleUp(std::vector<int> &heap, uint32_t root, uint32_t heapSize, uint32_t position,
                            const CmpFunc &cmpFunc) {
    if (heapSize <= 1) {
        return heapSize;
    }

    int value = heap[position];

    int j = position;
    while (j / 2 >= root) {
        int parent = j / 2;
        if (cmpFunc(value, heap[parent])) {    // value < heap[parent]
            std::swap(heap[j], heap[parent]);
            j /= 2;
        } else {
            break;
        }
    }
    heap[j] = value;

    return j;
}

uint32_t PriorityQueue::Size() const {
    return mHeapSize;
}

bool PriorityQueue::IsLeaf(uint32_t pos) {
    return (pos * 2) > Size();
}

int PriorityQueue::DeleteNodeAt(uint32_t pos) {
    if (pos > Size() || pos == 0) {
        throw std::out_of_range("HeapSize: " + std::to_string(mHeapSize) + ", pos: " + std::to_string(pos));
    }

    if (pos == Size()) {
        mHeapSize--;
        return mHeap[pos];
    }

    int value = mHeap[pos];

    mHeap[pos] = mHeap[mHeapSize];
    mHeapSize--;

    bubbleDown(mHeap, mHeapSize, pos, mCmpFunc);

    return value;
}

PriorityQueue::ValueType PriorityQueue::ValueAt(uint32_t pos) {
    if (pos > Size() || pos == 0) {
        throw std::out_of_range("HeapSize: " + std::to_string(mHeapSize) + ", pos: " + std::to_string(pos));
    }
    return mHeap[pos];
}

PriorityQueue::CmpFunc PriorityQueue::GetCmpFunc() const {
    return mCmpFunc;
}

对Heap抽象一个iterator不是现实的。

下面是一个固定大小的堆。它可以用来从一列数中找出最小的n个数。存储空间是O(1)。时间复杂度是O(n)

//.h

#ifndef AGORITHM_FIXEDSIZEPRIORITYQUEUE_H
#define AGORITHM_FIXEDSIZEPRIORITYQUEUE_H


#include "PriorityQueue.h"

// FixedPriorityQueue

// insertion time: if Size() > fixedSize: O(fixedSize) else: O(log(fixedSize))

class FixedPriorityQueue : public PriorityQueue {
public:
    explicit FixedPriorityQueue(uint32_t fixedSize);

    FixedPriorityQueue(uint32_t fixedSize, const CmpFunc &func);

    void Push(int value) override;

private:
    uint32_t mFixedSize = 0;
};


#endif //AGORITHM_FIXEDSIZEPRIORITYQUEUE_H

// .cpp
#include <iostream>
#include "FixedPriorityQueue.h"

FixedPriorityQueue::FixedPriorityQueue(uint32_t fixedSize) : FixedPriorityQueue(fixedSize, nullptr) {
}

FixedPriorityQueue::FixedPriorityQueue(uint32_t fixedSize, const PriorityQueue::CmpFunc &func)
        : PriorityQueue(fixedSize + 1, func) {
    mFixedSize = fixedSize;
}

void FixedPriorityQueue::Push(int value) {
    PriorityQueue::Push(value);

    const uint32_t N = Size();
    if (N > mFixedSize) {
        auto cmp = GetCmpFunc();
        auto minValue = ValueAt(N);
        uint32_t j = N - 1;
        uint32_t minPos = j;
        for (; j >= FIRST_POS && IsLeaf(j); j--) {  // find the minimum value
            auto curr = ValueAt(j);
            if (cmp(minValue, curr)) {
                minPos = j;
                minValue = curr;
            }
        }

        if (minPos >= FIRST_POS) {
            DeleteNodeAt(minPos);
        }
    }
}

上一篇 下一篇

猜你喜欢

热点阅读