堆的应用

1.优先级队列

优先级队列是0个或多个元素的集合,每个元素都有一个优先权,对优先级队列执行的操作有(1)查找(2)插入一个新元素 (3)删除 一般情况下,查找操作用来搜索优先权最大的元素,删除操作用来删除该元素 。
优先级队列是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。
优先队列插入元素的复杂度都是O(lgn),删除元素的复杂度是O(1),所以很快。

代码实现:

“Heap.h”
#pragma once
#include <vector>

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

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

//大堆
template <class T, class Compare = Greater<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.empty());
        swap(_a[0], _a[_a.size() - 1]);
        _a.pop_back();
        _AdjustDown(0);
    }
    const T& Top() const
    {
        return _a[0];
    }
    size_t Size()
    {
        return _a.size();
    }
    bool Empty()
    {
        return _a.empty;
    }
protected:
    void _AdjustDown(int root)//向下调整算法用于创建堆
    {
        Compare comFunc;
        int parent = root;
        int child = root * 2 + 1;
        while (child < _a.size())
        {
            if ((child+1<_a.size()) && comFunc(_a[child + 1],_a[child]))
            {
                ++child;
            }
            //到这里了,在大堆里child就是最大的那个孩子,小堆相反
            if (child<_a.size() && comFunc(_a[child] ,_a[parent]))//大堆里,孩子比父亲大
            {
                swap(_a[child], _a[parent]);//交换
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break;
            }
        }
    }
    void _AdjustUp(int child)//向上调整算法用于插入数据
    {
        Compare comFunc;
        int parent = (child - 1) / 2;
        while (child > 0)
        {
            if (comFunc(_a[child] , _a[parent]))
            {
                swap(_a[child], _a[parent]);
                child = parent;
                parent = (child - 1) / 2;
            }
            else
            {
                break;
            }
        }
    }
protected:
    vector<T> _a;
};

void TestHeap()
{
    int a[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };
    Heap<int> hp1(a, sizeof(a) / sizeof(a[0]));
    hp1.Push(25);
    hp1.Pop();
}

"PriorityQueue.h"
#pragma once
#include "Heap.h"

template <class T,class Compare = Greater<T>>//大的数优先级高
class PriorityQueue
{
public:
    PriorityQueue(T* a,size_t n)//构造函数
        :_hp(a,n)
    {}

    void Push(const T& x)//插入
    {
        _hp.Push(x);
    }

    void Pop()//删除
    {
        _hp.Pop();
    }

    const T& Top()//优先级最高的数
    {
        return _hp.Top();
    }
    size_t Size()//大小
    {
        return _hp.Size();
    }
    bool Empty()//是否为空
    {
        return _hp.Empty();
    }
protected:
    Heap<T, Compare> _hp;
};

void TestPriorityQueue()
{
    int a1[] = { 3, 7, 9, 2, 6, 1 };
    PriorityQueue<int> tp(a1, sizeof(a1) / sizeof(a1[0]));
    tp.Push(8);
    tp.Pop();
    tp.Pop();
    tp.Pop();
    tp.Pop();
    tp.Pop();
    tp.Pop();
    tp.Top();
}

"Test.cpp"
#include <iostream>
#include <assert.h>
using namespace std;

#include "Heap.h"
#include "PriorityQueue.h"

int main()
{
    TestPriorityQueue();
    return 0;
}

2.Topk问题

1.在1000个数中取10个数。(n中取k个数)
对于这问题的解决:
(1)、将这1000个数进行排序(升序或降序),然后取(后10个或前十个),用冒泡排序的时间复杂度O(n^2)
(2)、将这1000个数建大堆,依次取出前10个数。时间复杂度O(nlgn)
2、在10000000亿个数中取50个数。
如果这个用上面两种方法显然不能实现,因为内存中就存不下那么多数据。
这也叫海量数据处理。
这个也有两种方法:
(1)、建一个k大小的数组,然后进行排序。将剩余的数依次和数组中最小的数进行比较,如果比数组中最小的大,就进行交换。每次交换完后进行重新排序。时间复杂度O(nk)。
(2)、建一个K大小的小堆。将剩余的数依次和堆顶数据进行比较,如果比堆顶数据大就进行交换。每次交换完再调整为大堆。时间复杂度O(nlgk)。
如果k小一点,两个算法差不多。如果k大一点,显然建堆的时间复杂度小。

我写了建k大小的堆。
代码实现:

"TopK.h"
#pragma once

void AdjustDown(int* heap, size_t k, size_t parent)//向下调整算法
{
    size_t child = parent * 2 + 1;
    while (child < k)
    {
        if (child + 1<k && heap[child + 1] < heap[child])
        {
            ++child;
        }
        if (heap[child] < heap[parent])
        {
            swap(heap[parent], heap[child]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

void TopK(int* a, int n, int k)//在N个数中找最大K个数
{
    assert(n > k);
    int* heap = new int[k];//申请一个k大小的数组
    for (size_t i = 0; i < k; ++i)//在N中取前k个数,赋值给heap
    {
        heap[i] = a[i];
    }
    //找大的数建小堆,找小的数建大堆
    for (int i = (k - 2) / 2; i >= 0; --i)
    {
        AdjustDown(heap, k, i);
    }
    for (size_t j = k; j < n; ++j)
    {
        if (a[j] > heap[0])
        {
            heap[0] = a[j];
            AdjustDown(heap, k, 0);
        }
    }
    for (size_t i = 0; i < k; ++i)
    {
        cout << heap[i] << " ";
    }
    cout << endl;
    delete[] heap;
}

void TestTopK()
{
    const int N = 1000;
    const int K = 8;
    int a[N];
    for (size_t i = 0; i < 1000; ++i)
    {
        a[i] = i;
    }
    a[20] = 10000;
    a[30] = 1010;
    a[400] = 2000;
    TopK(a, N, K);
}

"Test.cpp"
#include <iostream>
#include <assert.h>
using namespace std;

#include "TopK.h"

int main()
{
    TestTopK();
    return 0;
}


----------

堆排序

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的值总是小于(或者大于)它的父节点。
堆排序是一种选择排序,是利用堆这种二叉树的性质进行的排序。
堆排序进行两步:第一步建堆,第二步选数据。
初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树)。所谓的建堆就是,如果排升序就建大堆,排降序就建小堆。选数据就是如果排升序,建好大堆后,取出堆顶数据就是n个数的最大数,和数组最后一个数进行交换。然后调整堆使之变为大堆,再取堆顶元素,得到n 个元素中次小(或次大)的元素,和数组第n-1个数进行交换。依次类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。

代码实现:

"HeapSort.h"
#pragma once

void AdjustDown(int* a, size_t n, size_t parent)//向下调整算法
{
    size_t child = parent * 2 + 1;
    while (child < n)
    {
        if (child + 1 < n && a[child + 1] > a[child])
        {
            ++child;
        }
        if (child<n && a[child] > a[parent])
        {
            swap(a[parent], a[child]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

void HeapSort(int* a, size_t n)
{
    assert(a);
    //排升序建大堆,排降序建小堆
    for (int i = (n - 2) / 2; i >= 0; --i)
    {
        AdjustDown(a, n, i);
    }
    //选数据
    size_t end = n - 1;
    while (end > 0)
    {
        swap(a[0], a[end]);
        AdjustDown(a, end, 0);
        --end;
    }
}

void Print(int* a, size_t n)
{
    for (size_t i = 0; i < n; ++i)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}

void TestHeapSort()
{
    int a[8] = { 6, 1, 9, 3, 4, 0, 2, 7 };
    HeapSort(a, sizeof(a) / sizeof(a[0]));
    Print(a, sizeof(a) / sizeof(a[0]));
}

"Test.cpp"
#include <iostream>
#include <assert.h>
using namespace std;

#include "HeapSort.h"

int main()
{
    TestHeapSort();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值