堆排序

堆排序的基本原理我就不讲了,网上有很多,大家可以移步这个链接学习,值得注意的是:

堆排序的总体复杂度为O(nlogn),初始建堆的复杂度为O(n),每次重建堆的时间复杂度为O(logn);

堆排序不适合排序数目较少的情况,且是不稳定排序;

堆排序是建立在大顶堆和小顶堆基础之上的,而大顶堆和小顶堆是建立在完全二叉树之上的(虽然不需要建树,但隐式建树)。

好了,直接上代码(以下为大顶堆排序):

#include<iostream>
#include<vector>

using namespace std;

//交换
void swap(vector<int> &array, int index1, int index2)
{
    if(index1 == index2)
        return;
    array[index1] = array[index1] ^ array[index2];
    array[index2] = array[index1] ^ array[index2];
    array[index1] = array[index1] ^ array[index2];
}

//递归形式调整堆
void maxheap_adjust(vector<int> &array, int currentNode, int size)
{
    if(currentNode >= size) //根节点定义序号为0,故退出条件是大于等于size
        return;
    int maxindex = 2 * currentNode + 1; //设置当前最大值为当前节点的左孩子
    //如果右节点存在(右孩子存在则根据完全二叉树性质,左孩子必然存在,反之不成立,故这里的‘&&’前后顺序不能对调),且为最大值,则更新
    if(maxindex + 1 < size && array[maxindex + 1] > array[maxindex])
        ++maxindex; 
    if(maxindex < size && array[currentNode] < array[maxindex])
    {
        swap(array, currentNode, maxindex);
        maxheap_adjust(array, maxindex, size); //递归
    }
}

void build_maxheap(vector<int> &array)
{
    //从第一个不是叶子节点的节点开始(叶子节点不需要考虑,可以减少循环次数)
    for(int i = array.size() / 2 - 1; i >= 0; --i)
        maxheap_adjust(array, i, array.size());
}

void printarray(vector<int> array, int i)
{
    if(i == 0)
        cout << "原数组为:";
    else
        cout << "第" << i << "遍调整后结果:";
    for(auto it : array)
        cout << it << ' ';
    cout << endl;
}

//k可以用于指定找到给定数组中的前k大(当k == array.size()时,就是从小到大排序)
void maxheap_sort(vector<int> &array, int k)
{
    //先建大顶堆
    build_maxheap(array);
    
    //再将大顶堆首位(也就是当前数组中的最大值)与数组尾部替换,并调整大顶堆获得次大值
    int size = array.size();
    for(int i = k; i > 0; --i)
    {
        swap(array, 0, size - 1);
        printarray(array, k - i + 1);
        maxheap_adjust(array, 0, --size); //注意设定根节点序号为0,故从0开始调整
    }
}

int main()
{
    int n; //数组中元素个数
    while(cin >> n) //循环输入每一组数组,同时按下control + C键退出
    {
        vector<int> input;
        int temp;
        for(int i = 0; i < n; ++i)
        {
            cin >> temp; //接收键盘输入
            input.push_back(temp);
        }
        printarray(input, 0);
        maxheap_sort(input, input.size());
    }
    return 0;
}

输入输出如下(只测试了几个例子,要是有错,欢迎大家指出哈):

除了递归形式来调整堆外,还可以采用循环形式实现,如下(只是修改了maxheap_adjust函数):

#include<iostream>
#include<vector>

using namespace std;

//交换
void swap(vector<int> &array, int index1, int index2)
{
    if(index1 == index2)
        return;
    array[index1] = array[index1] ^ array[index2];
    array[index2] = array[index1] ^ array[index2];
    array[index1] = array[index1] ^ array[index2];
}

//循环形式调整堆
void maxheap_adjust(vector<int> &array, int currentNode, int size)
{
    if(currentNode >= size) //根节点定义序号为0,故退出条件是大于等于size
        return;
    
    while(2 * currentNode + 1 < size)
    {
        int maxindex = 2 * currentNode + 1; //设置当前最大值为当前节点的左孩子
        //如果右节点存在(右孩子存在则根据完全二叉树性质,左孩子必然存在,反之不成立,故这里的‘&&’前后顺序不能对调),且为最大值,则更新
        if(maxindex + 1 < size && array[maxindex + 1] > array[maxindex])
            ++maxindex;
        if(array[currentNode] > array[maxindex]) //不需要再后续调整了
            break;
        swap(array, currentNode, maxindex);
        currentNode = maxindex;
    }
}

void build_maxheap(vector<int> &array)
{
    for(int i = array.size() / 2 - 1; i >= 0; --i)
        maxheap_adjust(array, i, array.size());
}

void printarray(vector<int> array, int i)
{
    if(i == 0)
        cout << "原数组为:";
    else
        cout << "第" << i << "遍调整后结果:";
    for(auto it : array)
        cout << it << ' ';
    cout << endl;
}

//k可以用于指定找到给定数组中的前k大(当k == array.size()时,就是从小到大排序)
void maxheap_sort(vector<int> &array, int k)
{
    //先建大顶堆
    build_maxheap(array);
    
    //再将大顶堆首位(也就是当前数组中的最大值)与数组尾部替换,并调整大顶堆获得次大值
    int size = array.size();
    for(int i = k; i > 0; --i)
    {
        swap(array, 0, size - 1);
        printarray(array, k - i + 1);
        maxheap_adjust(array, 0, --size); //注意设定根节点序号为0,故从0开始调整
    }
}

int main()
{
    int n; //数组中元素个数
    while(cin >> n) //循环输入每一组数组,同时按下control + C键退出
    {
        vector<int> input;
        int temp;
        for(int i = 0; i < n; ++i)
        {
            cin >> temp; //接收键盘输入
            input.push_back(temp);
        }
        printarray(input, 0);
        maxheap_sort(input, input.size());
    }
    return 0;
}

当然对于小顶堆,只需要改几处大于小于符号即可,如下:

递归形式

//递归形式调整堆
void minheap_adjust(vector<int> &array, int currentNode, int size)
{
    if(currentNode >= size) //根节点定义序号为0,故退出条件是大于等于size
        return;
    int minindex = 2 * currentNode + 1; //设置当前最大值为当前节点的左孩子
    //如果右节点存在(右孩子存在则根据完全二叉树性质,左孩子必然存在,反之不成立,故这里的‘&&’前后顺序不能对调),且为最大值,则更新
    if(minindex + 1 < size && array[minindex + 1] < array[minindex])
        ++maxindex; 
    if(minindex < size && array[currentNode] > array[minindex])
    {
        swap(array, currentNode, minindex);
        minheap_adjust(array, minindex, size); //递归
    }
}

循环形式

//循环形式调整小顶堆
void minheap_adjust(vector<int> &array, int currentNode, int size)
{
    if(currentNode >= size) //根节点定义序号为0,故退出条件是大于等于size
        return;
    
    while(2 * currentNode + 1 < size)
    {
        int minindex = 2 * currentNode + 1; //设置当前最小值为当前节点的左孩子
        //如果右节点存在(右孩子存在则根据完全二叉树性质,左孩子必然存在,反之不成立,故这里的‘&&’前后顺序不能对调),且为最大值,则更新
        if(minindex + 1 < size && array[minindex + 1] < array[minindex])
            ++minindex;
        if(array[currentNode] < array[minindex]) //不需要再后续调整了
            break;
        swap(array, currentNode, minindex);
        currentNode = minindex;
    }
}

还有要注意的是,大顶堆实际上用于从小到大排序,或者用于获取前K小;而小顶堆用于从大到小排序,或者用于获取前K大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值