数据结构:堆(heap)

来源:互联网 发布:安卓模拟器知乎 编辑:程序博客网 时间:2024/06/16 10:36

堆就是一种完全二叉树
堆可分为大堆和小堆
大堆:所有父节点大于子节点。
小堆:所有父节点小于子节点。

这里写图片描述
由于它是一棵完全二叉树,其元素之间没有空隙,所以我们可以用一个vector来表示它
这里写图片描述

我们可以根据需要选择创建大堆还是小堆,我们用仿函数实现这个功能

//大堆template<class T>struct Greater{    bool operator()(const T& a, const T& b)    {        return a > b;    }};//小堆template<class T>struct Less{    bool operator()(const T& s, const T& b)    {        return a < b;    }};

堆主要有两种算法:向上调整算法和向下调整算法
在进行插入操作时,我们使用向上调整算法:

这里写图片描述

而在进行删除操作时,使用向下调整算法:
这里写图片描述

代码实现:

#include<iostream>  #include<vector>  #include<assert.h> using namespace std;template<class T>struct Greater{    bool operator()(const T& a, const T& b)    {        return a > b;    }};template<class T>struct Less{    bool operator()(const T& s, const T& b)    {        return a < b;    }};template<class T,class Compare = Greater<T>>class Heap{public:    Heap()    {}    Heap(T* a, int n)    {        _a.reserve(n);//增容(可以直接拷贝数据)          for (int i = 0; i < n; i++)        {            _a.push_back(a[i]);        }        //调整成堆          for (int j = (_a.size() - 2) / 2; j >= 0; --j)        {            //向下调整              _AjustDown(j);        }    }    void Push(const T& x)    {        _a.push_back(x);        _AjustUp(_a.size() - 1);    }    void Pop()    {        assert(!_a.empty());        swap(_a[0], _a[_a.size() - 1]);        _a.pop_back();        _AjustDown(0);    }    const T& Top()    {        return _a[0];    }    size_t Size()    {        return _a.size();    }    bool Empty()    {        return _a.empty();    }protected:    //push_heap算法:向上调整    void _AjustUp(int child)    {        assert(!_a.empty());        int parent = (child - 1) >> 1;        while (child>0)        {            //如果孩子节点的值大于父节点的值              Compare com;            if (com(_a[child] , _a[parent]))            {                swap(_a[child], _a[parent]);                child = parent;                parent = (child - 1) >> 1;            }            else            {                break;            }        }    }protected:    //pop_heap:向下调整算法    void _AjustDown(int root)    {        int parent = root;        int child = parent * 2 + 1;        while (child < _a.size())        {            Compare com;            if ((child + 1 < _a.size()) &&( _a[child + 1] > _a[child]))            {                ++child;            }            if (com(_a[child] , _a[parent]))            {                swap(_a[child], _a[parent]);                parent = child;                child = parent * 2 + 1;            }            else            {                break;            }        }    }private:    vector<T> _a;};

测试:

void Test(){    int a[] = { 1, 6, 8, 2, 3, 5, 7, 4, 9 };    int len = sizeof(a) / sizeof(a[0]);    Heap<int, Greater<int>>  h(a, len);    h.Top();    h.Push(25);    h.Pop();    while (!h.Empty())    {        cout << h.Top() << " ";        h.Pop();    }    cout << endl;}

这里写图片描述

那么,堆有什么应用呢?
1,TopK问题
在一大堆数字中找到K个最大或最小的数字。
在现实中,很多场景都类似于这样的问题,比如,选出一个专业某科成绩排名前K的同学等等。这个时候,我们就可以利用一个小堆来进行筛选,堆顶为最小元素,比堆顶大(或小)的则进堆,重新建成一个小堆。以此类推,便形成了一个有K个元素的小堆。

void TopK(int* arr, size_t k,int n){    Heap<int, Less<int>> hp(arr,k);    for (size_t i = 0; i < n; i++)    {        if (arr[i] < hp.Top())//比堆顶数据大的则入堆        {            hp.Push(arr[i]);            hp.Pop();//取出堆顶数据        }    }}int main(){    //Test();    int arr[1000];    int sz = sizeof(arr) / sizeof(arr[0]);    for (size_t i = 0; i < sz; ++i)    {        arr[i] = rand() % 200;    }    arr[10] += 200;    arr[20] += 200;    arr[30] += 200;    arr[40] += 200;    arr[50] += 200;    arr[60] += 200;    arr[70] += 200;    arr[80] += 200;    TopK(arr, 8, sz);    system("pause");    return 0;}

2,堆排

//堆排void AjustDown(int *a, size_t root, size_t n){    size_t parent = root;    size_t child = parent * 2 + 1;    while (child < n)    {        if ((child + 1 < n) && (a[child + 1] > a[child]))        {            child++;        }        if (a[child] > a[parent])        {            swap(a[child], a[parent]);            parent = child;            child = parent * 2 + 1;        }        else        {            break;        }    }}void HeapSort(int *a,int n){    assert(a);    for (int i = (n-2)/2; i >= 0; i--)    {        AjustDown(a, i, n);    }    int end = n - 1;    while (end > 0)    {        swap(a[0], a[end]);        AjustDown(a, 0, end);        --end;    }}int main(){    int a[] = { 2, 4, 6, 7, 8, 9, 0, 5, 1, 3 };    int sz = sizeof(a) / sizeof(a[0]);    HeapSort(a, sz);    for (int i = 0; i < sz; i++)    {        cout << a[i] << " ";    }    cout << endl;    system("pause");    return 0;}