排序-堆排序

还不是很完整,后面会陆续补充  

 

堆的定义:

n个关键字序列L[0...n-1]称为堆,当且仅当该序列满足:

  1. L(i) <= L(2i + 1)  且 L(i) <= L(2i + 2)  ,此为最小堆。
  2. L(i) >= L(2i + 1)  且 L(i) >= L(2i + 2)  ,此为最大堆。

算法步骤:

  1. 利用给定数组创建一个堆H[0...n-1](假定以最大堆为例),输出堆顶元素。
  2. 将最后一个(堆低)元素送入堆顶,此时根节点不满足大顶堆的性质,堆被破坏,将堆顶元素向下调整使其继续保持大顶堆的性质,然后再输出堆顶元素。
  3. 如此重复,直到堆中仅剩一个元素为止。

调整最小堆: 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <bits/stdc++.h>
using namespace std;


void AdjustMinHeap(vector<int>& data, int pos, int right)
{

    int temp = data[pos];
    while(2 * pos + 1 <= right)
    {
        int child = 2 * pos + 1; // child 为需要调整的元素(下标为 pos )的左子节点的下标位置
        if(child + 1 <= right && data[child] > data[child +1] ) // 判断pos 是否存在右子节点,如果存在,则找出左子节点和右子节点的较小者
        {
            child = child + 1; // 右子节点比左子节点小,此时的位置更新为pos的右子节点
        }
        if(data[child] < temp)
        {
            data[pos] = data[child]; // 如果左右节点中较小的一个节点小于 pos 节点,则进行调整,也就是将这个较小的子节点赋值给父节点,
        }
        else
            break;  // 不需要调整,已经满足最小堆的性质了,所以跳出循环,
        pos = child; //如果上述条件满足,还需要依次往下判断调整。将调整的位置更新为从 child 开始
    }
    data[pos] = temp; //将最开始位置的节点赋值给最后不需要调整的节点。因为一开始调整的时候,父节点是被左右子节点中较小的一个节点直接覆盖的,
    // 然后子节点满足调整的条件的话,子节点又会被其左右子节点中较小的一个节点直接覆盖的。
}

void HeapSort(vector<int>& data)
{
    int len = data.size();
    for(int i = len/2 -1; i>=0; i--)//这里给定的关键字序列下标是从 0 开始计数的,所以从 (len/2-1)开始调整堆,即从最后一个元素的父节点开始。
    {
        AdjustMinHeap(data, i, len-1); //从最后一个元素的父节点开始建堆。
    }
    for(int i= len-1; i>=0; i--)
    {
        swap(data[i], data[0]); // 交换堆顶最小元素和序列的最后一个元素的位置。
        AdjustMinHeap(data, 0, i-1); // 这里从位置0处开始向下调整是因为,上一步刚刚交换了堆顶元素和最后一个元素,
        // 只是更换了堆顶的元素,所以只要向下调整堆顶元素到合适的位置就好了。
    }
}

int main()
{
    int numbers[] = {53, 17, 78, 9, 45, 65, 87, 32};
    vector<int>num(numbers,numbers+8);

    cout<<"数组的原来序列为:";
    vector<int>::iterator it = num.begin();
    for(it = num.begin(); it != num.end(); it++)
        cout<< *it <<' ';
    cout<< endl<<endl;

    // 堆排序
    HeapSort(num);

    cout <<"经过最小堆的调整后,(每次将最小值放在序列的最后),数组变为: ";
    for(it = num.begin(); it != num.end(); it++)
        cout<< *it <<' ';
    cout<< endl;
    return 0;
}
上面程序对应的结果

调整最大堆:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <bits/stdc++.h>
using namespace std;


void AdjustMaxHeap(vector<int>& data, int pos, int right)
{

    int temp = data[pos];
    while(2 * pos + 1 <= right)
    {
        int child = 2 * pos + 1; // child 为需要调整的元素(下标为 pos )的左子节点的下标位置
        if(child + 1 <= right && data[child] < data[child +1] ) // 判断pos 是否存在右子节点,如果存在,则找出左子节点和右子节点的较大者
        {
            child = child + 1; // 右子节点比左子节点大,此时的位置更新为pos的右子节点
        }
        if(data[child] > temp)
        {
            data[pos] = data[child]; // 如果左右节点中较大的一个节点大于 pos 节点,则进行调整,也就是将这个较大的子节点赋值给父节点,
        }
        else
            break;  // 不需要调整,已经满足最大堆的性质了,所以跳出循环,
        pos = child; //如果上述条件满足,还需要依次往下判断调整。将调整的位置更新为从 child 开始
    }
    data[pos] = temp; //将最开始位置的节点赋值给最后不需要调整的节点。因为一开始调整的时候,父节点是被左右子节点中较大的一个节点直接覆盖的,
    // 然后子节点满足调整的条件的话,子节点又会被其左右子节点中较大的一个节点直接覆盖的。
}

void HeapSort(vector<int>& data)
{
    int len = data.size();
    for(int i = len/2 -1; i>=0; i--)//这里给定的关键字序列下标是从 0 开始计数的,所以从 (len/2-1)开始调整堆,即从最后一个元素的父节点开始。
    {
        AdjustMaxHeap(data, i, len-1); //从最后一个元素的父节点开始建堆。
    }
    for(int i= len-1; i>=0; i--)
    {
        swap(data[i], data[0]); // 交换堆顶最小元素和序列的最后一个元素的位置。
        AdjustMaxHeap(data, 0, i-1); // 这里从位置0处开始向下调整是因为,上一步刚刚交换了堆顶元素和最后一个元素,
        // 只是更换了堆顶的元素,所以只要向下调整堆顶元素到合适的位置就好了。
    }
}

int main()
{
    int numbers[] = {53, 17, 78, 9, 45, 65, 87, 32};
    vector<int>num(numbers,numbers+8);

    cout<<"数组的原来序列为:";
    vector<int>::iterator it = num.begin();
    for(it = num.begin(); it != num.end(); it++)
        cout<< *it <<' ';
    cout<< endl<<endl;

    // 堆排序
    HeapSort(num);

    cout <<"经过最大堆的调整后,(每次将最大值放在序列的最后),数组变为: ";
    for(it = num.begin(); it != num.end(); it++)
        cout<< *it <<' ';
    cout<< endl;
    return 0;
}




借助C++ STL库的堆实现:

1,使用heap相关的算法:

  1.  make_heap(): 根据指定的迭代器区间以及一个可选的比较函数,来创建一个heap. 时间复杂度O(N)

  2. push_heap(): 把指定区间的最后一个元素插入到heap中. 时间复杂度O(logN)

  3. pop_heap(): 弹出heap顶元素, 将其放置于区间末尾. 时间复杂度O(logN)

  4. sort_heap():堆排序算法,通常通过反复调用pop_heap()来实现.  时间复杂度N*O(logN)

C++11 加入两个新成员:

  1. is_heap: 判断给定区间是否是一个heap. 时间复杂度NO(N)
  2. is_heap_until: 找出区间中第一个不满足heap条件的位置. 时间复杂度NO(N)

应用:剑指offer-面试题 41:数据流中的中位数

2,使用优先队列(priority_queue):

参考:【原创】优先队列 priority_queue 详解

应用:剑指offer:滑动窗口的最大值 解法二

参考文献

  1. 堆排序算法(图解详细流程)
  2. 维基百科-堆排序
  3. 【C++ STL应用与实现】72: 标准库里的堆--如何使用标准库的heap算法
  4. 【原创】优先队列 priority_queue 详解
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值