• 堆(Heap)


      两种简单实现

      第一种 链表

      第一种实现利用链表存储数据,每次在表头插入元素;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 中的关键字,根节点除外(它没有父亲)。

      代码实现

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    template<typename T>
    class Heap
    {
    public:
        Heap() : _v(1) {};
    
        void insert(T val)
        {
            _v.push_back(val);
    
            vector<T>::size_type i;
    
            for (i = _v.size() - 1; _v[i / 2] > val; i /= 2)
            {
                _v[i] = _v[i / 2];
            }
    
            _v[i] = val;
        }
    
        T removeMin()
        {
            if (_v.size() == 1)
            {
                return NULL;
            }
    
            T minElement = _v[1];
            T lastElement = _v[_v.size() - 1];
    
            vector<T>::size_type i, child;
    
            for (i = 1; i * 2 < _v.size(); i = child)
            {
                child = i * 2;
    
                if (child != _v.size() - 1 && _v[child + 1] < _v[child])
                {
                    child++;
                }
    
                if (lastElement > _v[child])
                {
                    _v[i] = _v[child];
                }
                else
                {
                    break;
                }
            }
    
            _v[i] = lastElement;
            _v.pop_back();
    
            return minElement;
        }
    
    private:
        vector<T> _v;
    };
    
    int main()
    {
        int arr[] = { 4,3,5,2,6 };
    
        Heap<int> h;
    
        for (auto e : arr)
        {
            h.insert(e);
        }
    
        for (auto e : arr)
        {
            cout << h.removeMin() << endl;
        }
    
        return 0;
    }
    View Code

      d-堆

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

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

      有证据显示,在实践中 4-堆 可以胜过二叉堆。

  • 相关阅读:
    证明欧几里得算法的正确性
    基础练习 十六进制转换八进制
    算法训练 关联矩阵
    寻找数组中最大值
    Torry的困惑(基本型)
    最小乘积(基本型)
    矩阵乘法
    SaltStack Char02 组件
    SaltStack Char01
    Python 学习手册, char 14
  • 原文地址:https://www.cnblogs.com/fengyubo/p/5186745.html
Copyright © 2020-2023  润新知