C++构建堆结构

堆是一种数据结构,本质上是数组,可以被视为一个完全二叉树结构。其又可以分为大堆和小堆。
大堆:每个父节点都大于孩子节点。
小堆:每个父节点都小于孩子节点。
这里写图片描述

以下由C++构成一个堆。

#include<vector>
#include<assert.h>
#include<iostream>
using namespace std;

template<class T>
struct Less
{
    bool operator()(const T& l,const T& r)
    {
        return l<r;
    }
};

template<class T>
struct Greater
{
    bool operator()(const T& l,const T& r)
    {
        return l>r;
    }
};
template<class T,class compare = Less<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);
        }

    }
    void Push(const T& x)
    {
        _a.push_back(x);
        _AdjustUp(_a.size()-1);
    }
    void Pop()
    {
        assert(_a.size() > 0);
        swap(_a[0],_a[_a.size()-1]);
        _a.pop_back();
        _AdjustDown(0);
    }

    const T& Top()
    {
        assert(!_a.empty());
        return _a[0];
    }

    size_t Size()
    {
        return _a.size();
    }

    bool Empty()
    {
        return _a.empty();
    }

protected:
    void _AdjustDown(int root)  //向下调整
    {
        compare com;
        int parent = root;
        int child = parent*2+1;

        while(child < _a.size())  // 孩子的下调范围不能超过数组大小
        {
            //if(child+1 < _a.size() && _a[child+1] > _a[child])
            if(child+1 < _a.size() && com(_a[child+1] , _a[child]))
            {
                child++;  //取子节点中最大的
            }

            //if(_a[parent] < _a[child])
            if(com( _a[child],_a[parent]))
            {
                swap(_a[parent],_a[child]);
                parent = child;
                child = parent*2+1;
            }
            else
            {
                break;
            }
        }

    }

    void _AdjustUp(int child)
    {
        compare com;
        int parent = (child-1)/2;

        while(child > 0)
        {
            //if(_a[child] > _a[parent])
             if(com(_a[child] , _a[parent]))
            {
                swap(_a[child],_a[parent]);
                child = parent;
                parent = (child-1)/2;
            }
            else
            {
                break;
            }
        }
    }

private:
    vector<T> _a;
};

其中的向上调整算法和向下调整算法。是关键。同时为了加强程序的复用功能,加入了仿函数。
所谓的向上调整,就是从最后一个叶节点的父节点开始,进行向上调整,当父节点小于孩子节点里最大的孩子节点时,将父节点和孩子节点互换,然后减到下一个节点,一次重复此过程。时间复杂度就是lgk.当进行最后一次的交换时,前提是左右子树已经成为了大堆。
向下调整,从最后一个节点和其父节点比较,如果大于其父节点,就交换,条件是孩子的位置不能小于数组的大小。

到了这里换必须再说一个问题,优先级队列。我们可以用对来构造一个。

template<class T, class Comapre>
class PriorityQueue
{
public:
    void Push(const T& x)
    {
        _hp.Push(x);
    }

    void Pop()
    {
        _hp.Pop();
    }

    const T& Top()
    {
        return _hp.Top();
    }
protected:
    Heap<T, Comapre> _hp;
};

接下来就是不能不说的排序问题了。
对于堆排序,升序建大堆,先把排序的数建堆,此时堆头的数时最大的,将其与最后一个互换,在进行自顶向下的调整(此时左右子树都是大堆),调整后堆头的数又是最大的,在进行一次刚才的操作,直至处理完。
降序建小堆(方法同升序)。

void Adjustdown(int *a,int n,int root)
{
    int parent = root;
    int child = parent*2+1;

    while(child < n)
    {
        if(child+1 < n && a[child] > a[child+1])
        {
            child++;
        }

        if(a[child] < a[parent])
        {
            swap(a[child],a[parent]);
            parent = child;
            child = parent*2+1;
        }
        else
        {
            break;
        }
    }
}

void heapsort(int* a,size_t n)
{
   //降序  构建小堆
    for(int i = (n-2)/2;i >= 0; i--)
    {
        Adjustdown(a,n,i);
    }

    int end = n-1;
    while(end > 0)
    {
        swap(a[0],a[end]);
        Adjustdown(a,end,0);
        end--;
    }

}

void TestHeapSort()
{
    int a[] = {2,3,6,8,9,3,5,6,10};
    heapsort(a, sizeof(a)/sizeof(a[0]));
}

堆的另一个应用就是解决海量数据处理。
例如:
(1)1 0 0 w个数中找出最大的前K个数
(2) 2 0 1 5 年春节期间,A公司的支付软件某宝和T公司某信红包大乱战。春节后高峰以后,公司L eader 要求后台的攻城狮对后台的海量
数据进行分析。先要求分析出各地区发红包金额最多的前1 0 0 用户。现在知道人数最多的s 地区大约有1 0 0 0 w用户。要求写一个算法
实现。【扩展:海量数据处理】
就第一个简单说一下。找出最大的K个数,在100W个数里,采用很高的效率,无疑一个好的方法就是建堆。先建立一个k个数的小堆,堆顶的元素最小,然后将K~100W的元素逐一比较,如果大于堆顶的元素就和堆顶的元素交换,然后在进行一次向下调整。话不多少,线上代码,帮你理解我的问题。

void Adjustdown(vector<int>& heap,int root)
{  //建立小堆
    int parent = root;
    int child = parent*2+1;

    while(child < heap.size())
    {
        if(child+1 < heap.size() && heap[child] > heap[child+1])
        {
            child++;
        }

        if(heap[child] < heap[parent])
        {
            swap(heap[child],heap[parent]);
            parent = child;
            child = parent*2+1;
        }
        else
        {
            break;
        }
    }
}

void TopK( vector<int>& a,int k)
{
    assert(a.size() > k);
    vector<int> topk;
    topk.reserve(k);
    for(int i = 0; i < k; i++)
    {
        topk.push_back(a[i]);
    }

    for(int i = (k-2)/2; i >= 0; i--)
    {
        Adjustdown(topk,i);
    }

    for(int i = k; i < a.size();i++)
    {
        if(topk[0] < a[i])
        {
         topk[0] = a[i];
         Adjustdown(topk,0);
        }

    }

    for(int i = 0; i < k; i++)
    {
        cout<<topk[i]<<" ";
    }
    cout<<endl;
}

void testBig()
{
    vector<int> a;
    a.resize(10000,10);
    a[99] = 99;
    a[199] = 199;
    a[299] = 299;
    a[399] = 399;
    a[4] = 100;
    a[5] = 101;
    a[6] = 102;
    a[7] = 103;

    a[40] = 104;
    a[55] = 105;
    a[60] = 106;
    a[70] = 107;
    TopK(a,20);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值