堆的基本操作

堆的創建及插入刪除堆頂元素等基本操作

  • 用模板參數來調整大小堆
#include<stdio.h>
#include<vector>
#include<assert.h>
#include<iostream>
using namespace std;


//下面是實現的小堆,同時我們也要實現大堆(只是將交換比較的小於號都改成大於號),爲了實現代碼的複用率,我們多添加一個模板參數---比較器(容器適配器)
//容器適配器的兩種實現:模板的模板參數,模板參數
template<class T>
struct Less//小堆
{
    bool operator()(const T& left,const T& right)//重載(),在裏面實現對<號的實現
    {
        return left<right;
    }
};

template<class T>
struct Greater
{
    bool operator()(const T& left,const T& right)
    {
        return left>right;
    }
};

//直接對數據進行操作
template<class T,class Complare = Less<T> >//模板參數實現比較器,Complare是一個類(Less 或則Greater)
class heap
{
public:
    heap()
        :_heap(NULL)
    {}
    heap(const T array[],size_t size)
    {
        //找到倒數第一個非葉子節點,即最後一個節點(下標爲i)的雙親節點(i-1)/2
        //從該節點的位置開始判斷其最小子節點是否小於雙親節點,即向下調整判斷法
        //若小於,則交換位置,並且向下判斷,更新雙親節點爲子節點,重複執行上面的代碼
        //若大於,則不交換,判斷上一個節點(層序遍歷倒數第二個節點),退出當前節點的遍歷

        //爲什麼不能用數組???

        _heap.resize(size);
        for(int idx=0; idx<_heap.size(); ++idx)//初始化容器
            _heap[idx] = array[idx];
          //_heap.push_back(array[idx]);

        int parent = (size-2)>>1;
        for(; parent>=0; --parent)
        {
            AdjustDown(_heap,parent);
        }
    }

    size_t Size()const
    {
        return _heap.size();
    }

    bool Empty()const
    {
        return _heap.empty();
    }

    const T& Top()const
    {
        return _heap[0];
    }
    void Insert(const T& value)//堆的任意插入
    {
        _heap.push_back(value);
        if(_heap.size()>1)
        {
            AdjustUp();
        }
    }

    void Remove()//刪除堆頂的元素
    {
        //刪除堆頂的元素,將堆頂的元素和最後一個節點進行交換,然後push_back最後一個元素,利用向下調整的方法調整小堆
        assert(!_heap.empty());
        int last = _heap.size()-1;
        swap(_heap[0],_heap[last]);
        _heap.pop_back();

        AdjustDown(_heap,0);//因爲現在只有根節點發生改變,左右子樹還是滿足最小堆的條件的,所以我們只要調整根節點就好
                            //利用向下調整的方法(注意這和堆的創建是不同的,堆的創建是從倒數第一個非葉子節點開始調整)
        //int parent = (_heap.size()-2)>>1;
        //for(; parent>=0; --parent)
        //{
        //  AdjustDown(_heap,parent);
        //}
    }
private:
    void AdjustDown(vector<T>& _heap,int parent)//向下調整
    {
        Complare com;//比較器,當比較器是Less(默認)是小堆,當比較器是Greater是大堆
        int child = parent*2+1;
        while(child<_heap.size())
        {
            if((child+1)<_heap.size() && com(_heap[child+1],_heap[child]))//當_heap[child]>_heap[child+1]時,才進入循環,交換左右參數順序
            {
                child = child+1;
            }

            if(com(_heap[child],_heap[parent]))//當_heap[child]<_heap[parent]時,進入判斷判斷體
            {
                swap(_heap[parent],_heap[child]);
                parent = child;
                child = 2*parent+1;
            }
            else
            {
                return;
            }
        }
    }

    void AdjustUp()
    {
        Complare com;
        size_t child = _heap.size()-1;//插入節點的下標,也是堆中最後一個節點
        size_t parent = (child-1)>>1;//倒數第一個非葉子節點

        while(child != 0)
        {
            if(com(_heap[child],_heap[parent]))//插入節點的兄弟節點一定比雙親節點大(小堆)
            {
                swap(_heap[child],_heap[parent]);
                child = parent;
                parent = (child-1)>>1;
            }
            else
            {

                return;
            }
        }
    }
private:
    vector<T> _heap;
};
  • 用模板的模板參數實現大小堆
template<class T,template<class> class Complare =Less >//模板的模板參數實現比較器,Complare是一個模板類
class heap
{
public:
    heap()
        :_heap(NULL)
    {}
    heap(const T array[],size_t size)
    {
        //找到倒數第一個非葉子節點,即最後一個節點(下標爲i)的雙親節點(i-1)/2
        //從該節點的位置開始判斷其最小子節點是否小於雙親節點,即向下調整判斷法
        //若小於,則交換位置,並且向下判斷,更新雙親節點爲子節點,重複執行上面的代碼
        //若大於,則不交換,判斷上一個節點(層序遍歷倒數第二個節點),退出當前節點的遍歷

        //爲什麼不能用數組???

        _heap.resize(size);
        for(int idx=0; idx<_heap.size(); ++idx)//初始化容器
            _heap[idx] = array[idx];
          //_heap.push_back(array[idx]);

        int parent = (size-2)>>1;
        for(; parent>=0; --parent)
        {
            AdjustDown(_heap,parent);
        }
    }

    size_t Size()const
    {
        return _heap.size();
    }

    bool Empty()const
    {
        return _heap.empty();
    }

    const T& Top()const
    {
        return _heap[0];
    }
    void Insert(const T& value)//堆的任意插入
    {
        _heap.push_back(value);
        int parent = (_heap.size()-2)>>1;
        //AdjustUp(_heap,parent);
        AdjustUp();
    }

    void Remove()//刪除堆頂的元素
    {
        //刪除堆頂的元素,將堆頂的元素和最後一個節點進行交換,然後push_back最後一個元素,利用向下調整的方法調整小堆
        int last = _heap.size()-1;
        swap(_heap[0],_heap[last]);
        _heap.pop_back();

        AdjustDown(_heap,0);//因爲現在只有根節點發生改變,左右子樹還是滿足最小堆的條件的,所以我們只要調整根節點就好
                            //利用向下調整的方法(注意這和堆的創建是不同的,堆的創建是從倒數第一個非葉子節點開始調整)
        //int parent = (_heap.size()-2)>>1;
        //for(; parent>=0; --parent)
        //{
        //  AdjustDown(_heap,parent);
        //}
    }
private:
    void AdjustDown(vector<T>& _heap,int parent)//向下調整
    {
        int child = parent*2+1;
        while(child<_heap.size())
        {//建立一個類模板的無名對象調用()重載函數
            if((child+1)<_heap.size() && Complare<T>()(_heap[child+1],_heap[child]))//當_heap[child]>_heap[child+1]時,才進入循環,交換左右參數順序
            {
                child = child+1;
            }

            if(Complare<T>()(_heap[child],_heap[parent]))//當_heap[child]<_heap[parent]時,進入判斷判斷體
            {
                swap(_heap[parent],_heap[child]);
                parent = child;
                child = 2*parent+1;
            }
            else
            {
                return;
            }
        }
    }

    void AdjustUp()
    {
        size_t child = _heap.size()-1;//插入節點的下標,也是堆中最後一個節點
        size_t parent = (child-1)>>1;//倒數第一個非葉子節點

        while(child != 0)
        {
            if(Complare<T>()(_heap[child],_heap[parent]))//插入節點的兄弟節點一定比雙親節點大(小堆)
            {
                swap(_heap[child],_heap[parent]);
                child = parent;
                parent = (child-1)>>1;
            }
        }
    }
private:
    vector<T> _heap;
};
發佈了109 篇原創文章 · 獲贊 17 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章