堆排序

利用STL的priority_queue< T >结构完成堆排序

堆排序的堆不是指内存区域里面的堆,而是一种数据结构。堆可以分为“大顶堆”和“小顶堆”,堆其实是一种特殊的二叉树,“大顶堆”中根元素总比叶子元素大,“小顶堆”中根元素总比叶子节点小。当然要创建这样的堆结构还是有一定难度的,请自行参考其他资料。
这里我们使用到的是 STL 中的 priority_queue< T> 这个结构,这个默认就是一个“大顶堆”,所以用这个数据结构我们来实现排序就很简单了。

代码:

#pragma region 堆排序
struct big
{
    bool operator()(const int &t1, const int &t2)
    {
        return t1 < t2;//大顶堆 
    }
};

struct small
{
    bool operator()(const int &t1, const int &t2)
    {
        return t1 > t2;//小顶堆 
    }
};

void heapSort(int arr[], int len)
{
    priority_queue<int,vector<int>,small> pq;
    for (int i = 0; i < len; ++i)
    {
        pq.push(arr[i]);
    }
    for (int i = 0; i < len; ++i)
    {
        arr[i] = pq.top();
        pq.pop();
    }
}
#pragma endregion

注意在创建堆结构的时候, priority_queue<int,vector<int>,small> pq;,使用自定义结构 small 或者 big 可以指定堆为大顶堆还是小顶堆。我们要从小到大排序,所以这里使用的是小顶堆。
由于使用到了 priority_queue 结构,排序过程很简单,就是将数组元素依次读入到 小顶堆中(注意这里构造小顶堆二叉树过程 priority_queue 已经帮我们完成),然后再将顶上元素依次放入到数组中,就已经排好序了。

完全二叉树堆排序

当然上面的方法是使用了STL中的结构,如果我们要自己写应该怎么办呢?

//调整数组的第i个节点,使节点i符合大顶堆
void adjustHeap(int *arr, int i, int size)
{
    if (2 * i + 1 <= size)
    {
        int maxi = i;
        if (arr[2 * i + 1] > arr[maxi])
        {
            maxi = 2 * i + 1;
        }
        if (2 * i + 2 <= size && arr[2 * i + 2] > arr[maxi])
        {
            maxi = 2 * i + 2;
        }
        if (maxi != i)
        {
            swap(arr[maxi], arr[i]);
            adjustHeap(arr, maxi, size);
        }
    }
}

// 遍历二叉树中所有非叶子节点,使整课树满足大顶堆
void buildHeap(int *arr, int size)
{
    for (int i = (size - 1) / 2; i >= 0; --i)
    {
        adjustHeap(arr, i, size);
    }
}

// 将大顶堆堆顶的数(最大值)与数组最后一个数交换,将前 size-1 个数再转换成大顶堆,依次循环下去,直到大顶堆只剩最后一个元素
void heapSort(int *arr, int size)
{
    buildHeap(arr, size);
    for (int i = size; i >= 1; --i)
    {
        swap(arr[i], arr[0]);
        buildHeap(arr, i - 1);
    }
}

看了上面的代码可能大家有些不明白,这里主要是要知道,这里存储二叉树的使用顺序结构(数组)存储的,我们将要排序的数组先初始化成一个完全二叉树,根据完全二叉树的性质。第i个节点如果有左右孩子,那么应该存储在 2*i+1, 2*i+1两个位置,将根,左孩子,右孩子比较,使根始终是最大的,遍历完所有的非叶子节点就可以将数组初始化为大顶堆,则就是 builheap() 和 adjustHeap() 函数的作用。至于 adjustHeap()函数中为什么还有一次递归调用,就是说调整3个上层的3个节点后,是无法保证调整后孩子节点一定与下面节点形成大顶堆,所以还需要将还下来的孩子节点再与现在它的孩子节点做一次调整。

堆排序也是一种不稳定的排序,最优时间复杂度为 nlog(n),但是实际用起来是相当的慢。因为涉及太多的调整堆结构过程。

你可能感兴趣的:(排序,堆排序)