堆实现及常见面试题

来源:互联网 发布:js获取当前时间显示 编辑:程序博客网 时间:2024/06/07 08:40
一、堆的概念:关键码的集合按完全二叉树的顺序存储方式存储在一维数组中,并满足:对于所有节点它的父节点关键码都大于子节点(或都小于它的子节点),这样的堆称为最大堆(或最小堆)。二、堆相关的问题1.简单实现一个堆 堆的建立、放入数据、删除数据、判断是否为堆2.TopK问题,求数组中最大的K个元素3.堆排序(实质是一个选择排序),一个数组的升序、降序 三、下面例子中,博主是以建最大堆的方式实现的堆,在实现TopK中需要建小堆,博主又另写了一个建小堆的函数。***Autually***, 这是一种较为笨的方法,可以实现一个仿函数,增加函数的复用性,这样建小堆建大堆就不用建不同的堆了。代码如下:
#pragma once#include <iostream>#include <vector>#define N 10000using namespace std;template<class T>class Heap{public:    Heap()    {}    Heap(T* a, size_t n)    {        _a.reserve(n);        for (size_t i = 0; i < n; i++)        {            _a.push_back(a[i]);        }        //建堆        for (int i = (_a.size() - 2) / 2; i >= 0; --i)//从倒数第一个非叶节点开始        {            AdjustDown(i);        }    }    HeapT(T* a, size_t k)//实现TopK建堆方式    {        _a.reserve(k);        for (size_t i = 0; i < k; i++)        {            _a.push_back(a[i]);        }        for (int i = (_a.size() - 2) / 2; i >= 0; --i)//建小堆        {            AdjustDownL(i);        }    }    void TopK(T* a)    {        for (size_t i = 100; i < N;)        {            if (_a[0] < a[i])            {                _a[0] = a[i];                AdjustDownL(0);                ++i;            }            else            {                ++i;            }        }    }    void HeapRiseSort(T* a, size_t k)//升序建大堆 降序降序建小堆    {        _a.reserve(k);        int end = k - 1;        Heap(a, k);//建堆        while (end > 0)        {            swap(_a[0], _a[end]);            end--;            AdjustDown(0);            //堆排序(升序),每次把堆顶数据放到数组后面,再利用前end个元素重新建大堆        }    }    void Print()    {        for (size_t i = 0; i < _a.size(); i++)        {            cout << _a[i] << " ";        }        cout << endl;    }        void Push(const T& x)        {            _a.push_back(x);            AdjustUp(_a.size()-1);        }        void AdjustUp(int child)        {            int parent = (child - 1) >> 1;            while (child != 0)            {                if (_a[child] > _a[parent])                {                    swap(_a[child], _a[parent]);                    child = parent;                    parent = (child - 1) >> 1;                }                else                {                    break;                }            }        }        void Pop()        {            swap(_a[0], _a[_a.size() - 1]);            _a.pop_back();            AdjustDown(0);        }        void AdjustDown(int root)        {            int parent = root;            int child = parent * 2 + 1;            while (child < _a.size())            {                if (child + 1 < _a.size() && _a[child + 1] > _a[child])//选出大的孩子(存在右孩子)                {                    ++child;                }                if (_a[child] > _a[parent])                {                    swap(_a[child], _a[parent]);                    parent = child;                    child = child * 2 + 1;                }                else                {                    break;                }            }        }        void AdjustDownL(int root)        {            int parent = root;            int child = parent * 2 + 1;            while (child < _a.size())            {                if (child + 1 < _a.size() && _a[child + 1] < _a[child])//选出大的孩子(存在右孩子)                {                    ++child;                }                if (_a[child] < _a[parent])//建最小堆                {                    swap(_a[child], _a[parent]);                    parent = child;                    child = child * 2 + 1;                }                else                {                    break;                }            }        }        int IsBHeap()        {            for (int i = _a.size() - 1; i >= 0; i++)            {                return _IsBHeap(i)==true;//为真返回1,假0            }        }protected:    bool _IsBHeap(int root)    {        int child = root;        int parent = (child - 1) >> 1;        while (child<_a.size())        {            int large = child;            if (child + 1<_a.size() && _a[child + 1]>_a[child])            {                large = child + 1;            }            if (_a[large] < _a[parent])            {                return true;            }            else            {                return false;            }        }        return false;    }    vector<T> _a;};    

测试用例:

#pragma once#include "Heap.h"void Heaptest(){    int arr[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };    Heap<int>h1(arr, sizeof(arr)/sizeof(arr[0]));    h1.Print();    h1.Pop();    h1.Print();    h1.Push(20);    h1.Print();    h1.Push(2);    cout << h1.IsBHeap() << endl;}void topKTest(){    int arr[N] = { 0 };    for (size_t i = 0; i < N; i++)    {        arr[i] = rand() % N;    }    arr[100] = 12000;    arr[999] = 11145;    arr[888] = 10008;    arr[118] = 19999;    arr[365] = 19880;    arr[254] = 13444;    arr[4579] = 14555;    arr[1000] = 47839;    arr[9992] = 19875;    arr[1723] = 98345;    HeapT<int>h1(arr, 10);    h1.TopK(arr);    h1.Print();}//堆排序实质上是一种选择排序,选出最大的与尾部数据交换,然后堆元素个数--,重新建堆,相当于没把刚刚选出的最大的数算在重新建的堆中void Heaptest(){    int arr[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };    Heap<int>h1(arr, 10);    h1.HeapRiseSort(arr,10);    h1.Print();}

堆就讲到这里啦,之后会给大家补充仿函数和堆的相关知识,拜拜~