C++实现堆排序并记录编程中遇到的一个bug(不要对无符号整形数在--的循环中采用>=0作为结束条件)

通过二叉堆的堆序性质,就能通过堆进行排序。

对于大顶堆,其树根为整个堆的最大值,对于小顶堆,其树根为整个堆的对小值。因此通过不断的获得堆顶,然后把堆最后一个元素放在堆顶,并重新维持堆序,反复如此,则获得的序列是有序的。建堆的时间花费大约为 O(N) ,每次执行取堆顶的操作(deleteMin或者deleteMax)花费时间为 O(logN) 。因此总的运行时间为 O(NlogN) 。而且经验指出,堆排序算法运行时间的稳定性很高,它平均的比较次数只比最坏的情形界指出的次数略少。

这个算法还有一个问题,就是每次取了堆顶需要放在另外一个数组中,因此对于空间有一定的要求。但是其实每次取了堆顶后,堆的大小缩小了1,因此可以把取出的堆顶放在堆中最后一个元素的位置,然后把最后的元素放在堆顶,重新实现堆序。因此,最终的顺序和取堆顶得到的顺序相逆。所以,如果要实现从小到大的排序,需要使用大顶堆。

记录一下今天的代码(并在最后看一个Bug):


#include<iostream>
#include<vector>
#include<random>
#include<ctime>
#include<iterator>
#include<algorithm>
using namespace std;

/*
* 获得元素的左儿子
* 由于从下标0开始,对于下标为i的元素
* 其左儿子为2*i+1, 右儿子为2*(i+1)
*/
inline int leftChild(int i)
{
    return 2 * i + 1;
}

/*
* 配合堆排序的下滤算法
* a为需要进行下滤的堆
* i为a中需要下滤的元素
* n为堆的大小
* (由于要直接在传入的数组上进行建堆并排序,
* 即或得大顶堆后把该元素放在空出来的那个位置上,
* 因此堆的大小在一直减小)
*/
template<typename T>
void percDown(vector<T> &a, int i, int n)
{
    int child;
    T tmp;
    for (tmp = a[i]; (child = leftChild(i)) < n; i = child)
    {
        //因为(child = leftChild(i)) < n,所以已经保证child<n
        //如果child != n - 1, 则节点i还有右儿子
        //因此要选出最大的儿子,就要和右儿子比较
        if (child != n - 1 && a[child] < a[child + 1])
            ++child;

        if (tmp < a[child])
            a[i] = a[child];
        else
            break;
    }
    a[i] = tmp;
}


/*
* 堆排序,使用大顶堆实现从小到大排序
* 不使用第二个向量进行中转,只利用传入的向量,节约空间
* 第一个元素放在向量位置为0的地方,即a[0]位第一个元素
*/
template<typename T>
void heapSort(vector<T> & a)
{
    //建立堆序
    //这里在写程序的时候遇到一个bug,开始定义变量i为size_t类型
    //然后程序总是崩溃,调试的时候发现,每次i=0运行完后,循环没有结束
    //而是继续执行,后面才反应过来,size_t为64位无符号整数
    //当它为0时,再执行--操作,不是-1了,而是最大的无符号整数了,
    //因此陷入死循环,程序崩溃
    for (int i = a.size() / 2; i >= 0; --i)
        percDown(a, i, a.size());
    //进行堆排序
    for (int j = a.size() - 1; j > 0; --j)
    {
        swap(a[0], a[j]);//把最大的元素(即堆顶)放在堆的最后一个位置上
        percDown(a, 0, j);//此时堆的大小减小一个,并且重建堆序
    }
}


/*输出向量*/
template<typename T>
void printVector(vector<T> & v)
{
    copy(v.cbegin(), v.cend(), ostream_iterator<T>(cout, " "));
    cout << endl;
}

int main()
{
    vector<int> source;
    uniform_int_distribution<int> u(0, 1000);
    default_random_engine e(static_cast<unsigned int>(time(0)));
    for (int i = 0; i < 30; i++)
    {
        source.push_back(u(e));
    }

    cout << "排序前:" << endl;
    printVector(source);

    heapSort(source);

    cout << "排序后:" << endl;
    printVector(source);

    return 0;
}

运行的结果为:
这里写图片描述

这个Bug是在编写heapSort()时出现的。由于开始使用了size_t表示向量的下标,即size_t i;此时,当i为0时,希望此时--i使i<0,然后跳出循环,可是此时i变成了另外一个数(该无符号整型的最大值,和其二进制位数有关),因此恒大于等零,陷入死循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值