【數據結構】堆的實現(包括:默認成員函數,插元素push,刪元素pop,訪問根節點top,判空,大小)

在數據結構裏,堆是一類很重要的結構。堆結構是一組數組對象,咱們能夠把它看成是一顆徹底二叉樹。ios


最大堆:堆裏每個父親節點大於它的子女節點。編程

最小堆:堆裏每個父親節點小於它的子女節點。數組

如圖就是一個最大堆:
數據結構

wKioL1cbOTSjDiLRAAAZq4jMjWY012.png

實現代碼時個人測試序列是:int a[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };ide

咱們把它的圖畫出來,便於分析。函數

wKioL1cbVunRJvBdAABYpvWkEaw905.png

咱們來實現如何將一個數組中的序列轉變爲最大堆。測試

若咱們知道最大堆的代碼後,只需將代碼稍微修改一下就能夠變成最小堆的代碼。或者,咱們能夠用仿函數來提升代碼的複用性。
spa

實現代碼以下:對象

創建頭文件heap.hpp
get

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

#include<assert.h>
#include<vector>

template <class T>
class Heap
{
public:
    Heap()
        :_a(NULL)
    {}


    //構造堆:先把各個元素接收到,再根據堆的特色將元素調整
    Heap(const T* array, size_t size)
    {
        _a.reserve(size);
        for (size_t i = 0; i < size; i++)
        {
            _a.push_back(array[i]);
        }

        //建堆
        int Size = size;
        for (int j = (_a.size() - 2) / 2; j>=0; j --)
        {
            _AdjustDown(j, Size);
        }
    }


    //拷貝構造
    Heap(const vector<T>& vec)
        :_a(NULL)
    {
        _a.reserve(vec.size());
        for (size_t i = 0; i < size; i++)
        {
            _a.push_back(vec[i]);
        }
    }

    //插入一個元素x:先插入到順序表中,再根據具體元素大小向上調整肯定插入元素的位置
    void Push(const T& x)
    {
        _a.push_back(x);
        _AdjustUp(_a.size() - 1);
    }


    //刪除根節點
    void Pop()
    {
        size_t size = _a.size();
        assert(size > 0);//防護式編程,肯定是否能夠刪除元素
        swap(_a[0], _a[size - 1]);//若直接刪除堆的根節點,則會使堆結構紊亂
        _a.pop_back();//將根節點與堆的最後一個節點交換位置,此時再對元素刪除,以及將其調整於合適位置
        size = _a.size();
        _AdjustDown(0,size);
    }


    //訪問堆的根節點
    T& GetTop()
    {
        size_t size = _a.size();
        assert(size > 0);
        return _a[0];
    }


    //將根節點向下調整
    void _AdjustDown(size_t parent,size_t size)
    {
        size_t child = 2 * parent + 1;
        while (child<size)
        {
            if (child+1 < size && _a[child] < _a[child + 1])
            {
                child++;
            }
            if (_a[child] > _a[parent])
            {
                swap(_a[child], _a[parent]);
                parent = child;
                child = 2 * parent + 1;
            }
            else
            {
                break;
            }
        }        
    }


    //向上調整
    void _AdjustUp(int child)
    {
        //不管插節點後爲左子樹仍是右子樹,均可用(child-2)/2計算出此時父節點的下標
        int parent = (child - 1) / 2;
        int size = _a.size();//size用int,若用size_t循環條件且爲>=0則死循環
        while (child>0)//當child=0,說明此時已經到根節點位置,無需繼續上調
        {
            //向上調整時,無需看左右節點哪一個值大,只須要看是否父節點<根節點
            if (_a[child]>_a[parent])
            {
                swap(_a[child], _a[parent]);
                child = parent;
                parent = (child-1)/2;
            }
            else
            {
                break;
            }
        }
    }


    bool Empty()
    {
        size_t size = _a.size();
        assert(size >= 0);
        return size == 0;
    }


    size_t Size()
    {
        size_t size = _a.size();
        assert(size >= 0);
        return size;
    }
    
    
    void PrintHeap()
    {
        cout << "堆的序列爲:" << endl;
        for (int i = 0; i < Size(); i++)
        {
            cout << _a[i] << "  ";
        }
        cout << endl;
    }
private:
    vector<T> _a;
};


創建源文件heap.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include "heap.hpp"

void Test()
{
    int a[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };
    Heap<int> h1(a, sizeof(a) / sizeof(a[0]));
    Heap<int> h2(h1);
    cout<<h1.GetTop()<<endl;
    cout << h1.Size() << endl;

    h1.Push(20);
    cout << h1.GetTop() << endl;

    h1.Pop();
    cout << h1.Size() << endl;

}


int main()
{
    Test();
    system("pause");
    return 0;
}


關於size(),GetTop()等函數咱們能夠經過測試函數Test()寫出適當的測試用例來測試。

相關文章
相關標籤/搜索