两种简单实现

  第一种 链表

  第一种实现利用链表存储数据,每次在表头插入元素;getMin 时,遍历一遍线性表找到最小的元素,然后将之删除、值返回。(getMax 同理)。

  链表的在头节点的插入和删除时间复杂度都是O(1),所以用链表实现的堆,insert 时间复杂度是O(1)、getMin 时间复杂度是O(n)。

  C++代码如下

template<typename T>
class Heap
{
public:
    void insert(const T &val)
    {
        l.push_front(val);
    }

    T getMin()
    {
        list<T>::iterator itMin = l.begin();

        for (list<T>::iterator it = l.begin(); it != l.end(); it++)
        {
            if (*it < *itMin)
                itMin = it;
        }

        T temp = *itMin;

        l.erase(itMin);

        return temp;
    }

private:
    list<T> l;
};
View Code

  测试用例如下

int main()
{
    Heap<int> h;

    h.insert(5);
    h.insert(10);
    h.insert(1);
    h.insert(20);

    cout << h.getMin() << endl;

    return 0;
}
View Code

  第二种 二叉树

  第二种实现利用二叉树来存储元素,它对于 insert 和 getMin 操作时间复杂度都是 O(log N)。

  C++代码如下

template<typename T>
struct Node
{
    Node(T v) : val(v), left(nullptr), right(nullptr) {};
    T val;
    struct Node* left;
    struct Node* right;
};

template<typename T>
class Heap
{
public:
    Heap() { root = nullptr; }

    void insert(const T &val)
    {
        struct Node<T> **p = &root;

        while (*p != nullptr)
        {
            if (val == (*p)->val)
                return;

            if (val < (*p)->val)
            {
                p = &((*p)->left);
                continue;
            }

            if (val >(*p)->val)
            {
                p = &((*p)->right);
                continue;
            }
        }

        *p = new struct Node<T>(val);
    }

    T getMin() 
    {
        struct Node<T> *p = findMin(root);
    
        T temp = p->val;

        erase(p);

        return temp;
    }

private:
    struct Node<T>* findMin(struct Node<T>* p)
    {
        if (p != nullptr)
            while (p->left != nullptr)
                p = p->left;

        return p;
    }

    struct Node<T>* findFather(T val)
    {
        struct Node<T>* p = root;

        while (p != nullptr)
        {
            if (p->val > val)
            {
                if (p->left->val == val)
                    break;

                p = p->left;
            }

            if (p->val < val)
            {
                if (p->right->val == val)
                    break;

                p = p->right;
            }

            if (p->val == val)
                break;
        }

        return p;
    }

    void erase(struct Node<T>* p)
    {
        struct Node<T>* fatherP = findFather(p->val);

        if (p == root)
        {
            if (p->left == nullptr && p->right == nullptr)
            {
                root = nullptr;
                delete p;
            }
            
            //right
            else if (p->left == nullptr && p->right != nullptr)
            {
                root = p->right;
                delete p;
            }
        }

        //leaf
        else if (p->left == nullptr  &&  p->right == nullptr)
        {
            if (fatherP->left == p)
                fatherP->left = nullptr;

            if (fatherP->right == p)
                fatherP->right = nullptr;

            delete p;
        }

        //right child
        else if (p->left == nullptr  &&  p->right != nullptr)
        {
            fatherP->left = p->right;
            delete p;
        }
    }

    struct Node<T>* root;
};
View Code

  测试用例如下

int main()
{
    Heap<int> bt;

    for (auto &e : { 8})
        bt.insert(e);

    for (auto &e : { 8 })
        cout << bt.getMin() << endl;
    
    for (auto &e : { 8, 3, 1 })
        bt.insert(e);

    for (auto &e : { 8, 3, 1 })
        cout << bt.getMin() << endl;

    for (auto &e : { 8, 9, 10 })
        bt.insert(e);

    for (auto &e : { 8, 9, 10 })
        cout << bt.getMin() << endl;

    for (auto &e : { 8, 5, 10, 4, 6, 9, 11 })
        bt.insert(e);

    for (auto &e : { 8, 5, 10, 4, 6, 9, 11 })
        cout << bt.getMin() << endl;

    return 0;
}
View Code

  二叉堆

  二叉堆是“堆”的默认实现方式。

  堆结构两大性质

  i. 结构性质

  对于数组中任一位置 i 上的元素,其左儿子在位置 2i 上,右儿子在左儿子后的单元 (2i + 1)中,它的父亲则在位置 ⌊i / 2⌋ 上。

  i. 堆序性质

  在一个堆中,对于每一个节点 X, X 的父亲中的关键字小于(或等于)X 中的关键字,根节点除外(它没有父亲)。

  代码实现

template<typename T>
class Heap
{
public:
    Heap(const unsigned maxElems = 2, const T &minData = -65535) 
        : v(maxElems), size(0), capacity(maxElems)
    {
        v[0] = minData;
    }

    void insert(const T &val)
    {
        unsigned i;
        
        if (++size == capacity)
            v.resize(capacity * 2);

        for (i = size; v[i / 2] > val; i /= 2)
            v[i] = v[i / 2];

        v[i] = val;
    }

    T getMin()
    {
        if (size == 0)
            return T();

        T minElem = v[1];
        T lastElem = v[size--];

        unsigned i, child;

        for (i = 1; i * 2 <= size; i = child)
        {
            child = i * 2;
            
            if (child != size && v[child + 1] < v[child])
                child++;

            if (lastElem > v[child])
                v[i] = v[child];
            else
                break;
        }

        v[i] = lastElem;
        return minElem;
    }

private:
    vector<T> v;
    unsigned size;
    unsigned capacity;
};
View Code

   测试用例

int main()
{
    Heap<int> h;

    for (auto &e : { 5, 4, 3, 6, 7 })
        h.insert(e);

    for (auto &e : { 5, 4, 3, 6, 7, 8 })
        cout << h.getMin() << endl;

    return 0;
}
View Code

  d-堆

  d-堆是二叉堆的简单推广,它恰像一个二叉堆,只是所有节点都有 d 个儿子(因此,二叉堆是2-堆)。

  它将 insert 操作的运行时间改进为 O(logd N)。然而,对于大的 d,deleteMin操作费时得多。

你可能感兴趣的:(堆)