C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ 大根堆排序

C++ 大根堆排序学习笔记

作者:Totn

这篇文章主要为大家介绍了C++ 大根堆排序的学习教程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

什么是大根堆

大根堆是完全二叉树,其中每个节点都比其子节点大,而根节点是最大的节点,所以称为“大根” 堆;

大根堆排序则是基于大根堆实现的排序算法,基本思想是将待排序的序列组成一个大根堆,然后依次取出顶元素(即最大元素),并将剩余元素构成新的大根堆,重复以上过程直到最后一个元素,就得到了完全排好序的序列。大根堆排序是一种原地排序算法,所以其空间复杂度为O(1),只需要常数级别的空间;而时间复杂度则为O(nlogn)

排序步骤

我们从序列的最后一个非叶子节点开始,依次将其与其子节点进行比较,如果有子节点比它大,则将它与最大的子节点交换位置,然后再以交换后的子节点作为根节点,继续向下比较和交换,直到该节点成为叶子节点或者它的子节点都比它小为止。这样就可以保证每个节点都比其子节点大,从而构建成一个大根堆。

由于大根堆的根节点是最大的元素,因此我们可以直接取出堆顶元素,并将其放到序列的末尾。

取出堆顶元素后,我们需要重新构建剩余元素的大根堆。具体操作是将序列的第一个元素作为根节点,依次将其与其子节点进行比较,如果有子节点比它大,则将它与最大的子节点交换位置,然后再以交换后的子节点作为根节点,继续向下比较和交换,直到该节点成为叶子节点或者它的子节点都比它小为止。这样就可以保证剩余元素构成一个新的大根堆。

C++代码实现

#include <iostream>
#include <vector>
// 打印数组
void printArr(std::vector<int>& arr, int len = 0);
// 大根堆排序主方法
void heapify(std::vector<int>&, int, int);
// 大根堆排序
void heapSort(std::vector<int>&);
// 大堆排序
void heapify(std::vector<int>& arr, int len, int index) {
    int largest = index;
    std::cout << "设置当前节点arr[" << index << "]=" << arr[index] << "为根节点" << std::endl;
    int left = 2 * index + 1;
    std::cout << "左节点索引: " << left << std::endl;
    int right = 2 * index + 2;
    std::cout << "右节点索引: " << right << std::endl;
    if (left < len) {
        std::cout << "比较左节点arr[" << left << "]=" << arr[left] << "与根点节大小" << std::endl;
    } else {
        std::cout << "左节点超出范围,即不存在" << std::endl;
    }
    if (left < len && arr[left] > arr[largest])
    {
        largest = left;
    }
   if (right < len) {
        std::cout << "比较右节点arr[" << right << "]=" << arr[right] << "与根点节大小" << std::endl;
    } else {
        std::cout << "右节点超出范围,即不存在" << std::endl;
    }
    if (right < len && arr[right] > arr[largest])
    {
        largest = right;
    }
    if (largest != index) {
        std::cout << "根节点小于子节点,交换根(arr[" << index << "]=" << arr[index] << ")与子(arr[" << largest << "]=" << arr[largest] << ")节点" << std::endl;
        std::swap(arr[index], arr[largest]);
        printArr(arr, len);
        heapify(arr, len, largest);
    }
}
void heapSort(std::vector<int>& arr) {
    int len = arr.size();
    // 构建大根堆, 从最后一个非叶子节点向下调整
    for (int i = len / 2 - 1; i >= 0; i--)
    {
        heapify(arr, len, i);
    }
    std::cout << "大根堆: ";
    printArr(arr);
    // 依次将堆顶元素与堆的最后一个元素交换,并调整堆
    for (int i = len - 1; i > 0; i--)
    {
        std::cout << "将最大值(arr[0]=" << arr[0] << ")与最后元素(arr[" << i << "]=" << arr[i] << ")交换" << std::endl;
        std::swap(arr[0], arr[i]);
        std::cout << "重新设置大根堆: ";
        printArr(arr, i);
        heapify(arr, i, 0);
    }
}
void printArr(std::vector<int>& arr, int len) {
    std::cout << "{ ";
    if (len <= 0) {
        for (auto &&n : arr)
        {
            std::cout << n << " ";
        }
    } else {
        for (int i = 0; i < len; i++)
        {
            std::cout << arr[i] << " ";
        }
    }
    std::cout << "}" << std::endl;
}
int main() {
    std::vector<int> arr = {1, 39, 2, 66, 23, 5, 6, 9, 4, 8};
    std::cout << "原数组: ";
    printArr(arr);
    heapSort(arr);
    std::cout << "排序后: ";
    printArr(arr);
}

排序过程

编译以上代码后执行,得到以下结果

原数组: { 1 39 2 66 23 5 6 9 4 8 }
设置当前节点arr[4]=23为根节点
左节点索引: 9
右节点索引: 10
比较左节点arr[9]=8与根点节大小
右节点超出范围,即不存在
设置当前节点arr[3]=66为根节点
左节点索引: 7
右节点索引: 8
比较左节点arr[7]=9与根点节大小
比较右节点arr[8]=4与根点节大小
设置当前节点arr[2]=2为根节点
左节点索引: 5
右节点索引: 6
比较左节点arr[5]=5与根点节大小
比较右节点arr[6]=6与根点节大小
根节点小于子节点,交换根(arr[2]=2)与子(arr[6]=6)节点
{ 1 39 6 66 23 5 2 9 4 8 }
设置当前节点arr[6]=2为根节点
左节点索引: 13
右节点索引: 14
左节点超出范围,即不存在
右节点超出范围,即不存在
设置当前节点arr[1]=39为根节点
左节点索引: 3
右节点索引: 4
比较左节点arr[3]=66与根点节大小
比较右节点arr[4]=23与根点节大小
根节点小于子节点,交换根(arr[1]=39)与子(arr[3]=66)节点
{ 1 66 6 39 23 5 2 9 4 8 }
设置当前节点arr[3]=39为根节点
左节点索引: 7
右节点索引: 8
比较左节点arr[7]=9与根点节大小
比较右节点arr[8]=4与根点节大小
设置当前节点arr[0]=1为根节点
左节点索引: 1
右节点索引: 2
比较左节点arr[1]=66与根点节大小
比较右节点arr[2]=6与根点节大小
根节点小于子节点,交换根(arr[0]=1)与子(arr[1]=66)节点
{ 66 1 6 39 23 5 2 9 4 8 }
设置当前节点arr[1]=1为根节点
左节点索引: 3
右节点索引: 4
比较左节点arr[3]=39与根点节大小
比较右节点arr[4]=23与根点节大小
根节点小于子节点,交换根(arr[1]=1)与子(arr[3]=39)节点
{ 66 39 6 1 23 5 2 9 4 8 }
设置当前节点arr[3]=1为根节点
左节点索引: 7
右节点索引: 8
比较左节点arr[7]=9与根点节大小
比较右节点arr[8]=4与根点节大小
根节点小于子节点,交换根(arr[3]=1)与子(arr[7]=9)节点
{ 66 39 6 9 23 5 2 1 4 8 }
设置当前节点arr[7]=1为根节点
左节点索引: 15
右节点索引: 16
左节点超出范围,即不存在
右节点超出范围,即不存在
大根堆: { 66 39 6 9 23 5 2 1 4 8 }
将最大值(arr[0]=66)与最后元素(arr[9]=8)交换
重新设置大根堆: { 8 39 6 9 23 5 2 1 4 }
设置当前节点arr[0]=8为根节点
左节点索引: 1
右节点索引: 2
比较左节点arr[1]=39与根点节大小
比较右节点arr[2]=6与根点节大小
根节点小于子节点,交换根(arr[0]=8)与子(arr[1]=39)节点
{ 39 8 6 9 23 5 2 1 4 }
设置当前节点arr[1]=8为根节点
左节点索引: 3
右节点索引: 4
比较左节点arr[3]=9与根点节大小
比较右节点arr[4]=23与根点节大小
根节点小于子节点,交换根(arr[1]=8)与子(arr[4]=23)节点
{ 39 23 6 9 8 5 2 1 4 }
设置当前节点arr[4]=8为根节点
左节点索引: 9
右节点索引: 10
左节点超出范围,即不存在
右节点超出范围,即不存在
将最大值(arr[0]=39)与最后元素(arr[8]=4)交换
重新设置大根堆: { 4 23 6 9 8 5 2 1 }
设置当前节点arr[0]=4为根节点
左节点索引: 1
右节点索引: 2
比较左节点arr[1]=23与根点节大小
比较右节点arr[2]=6与根点节大小
根节点小于子节点,交换根(arr[0]=4)与子(arr[1]=23)节点
{ 23 4 6 9 8 5 2 1 }
设置当前节点arr[1]=4为根节点
左节点索引: 3
右节点索引: 4
比较左节点arr[3]=9与根点节大小
比较右节点arr[4]=8与根点节大小
根节点小于子节点,交换根(arr[1]=4)与子(arr[3]=9)节点
{ 23 9 6 4 8 5 2 1 }
设置当前节点arr[3]=4为根节点
左节点索引: 7
右节点索引: 8
比较左节点arr[7]=1与根点节大小
右节点超出范围,即不存在
将最大值(arr[0]=23)与最后元素(arr[7]=1)交换
重新设置大根堆: { 1 9 6 4 8 5 2 }
设置当前节点arr[0]=1为根节点
左节点索引: 1
右节点索引: 2
比较左节点arr[1]=9与根点节大小
比较右节点arr[2]=6与根点节大小
根节点小于子节点,交换根(arr[0]=1)与子(arr[1]=9)节点
{ 9 1 6 4 8 5 2 }
设置当前节点arr[1]=1为根节点
左节点索引: 3
右节点索引: 4
比较左节点arr[3]=4与根点节大小
比较右节点arr[4]=8与根点节大小
根节点小于子节点,交换根(arr[1]=1)与子(arr[4]=8)节点
{ 9 8 6 4 1 5 2 }
设置当前节点arr[4]=1为根节点
左节点索引: 9
右节点索引: 10
左节点超出范围,即不存在
右节点超出范围,即不存在
将最大值(arr[0]=9)与最后元素(arr[6]=2)交换
重新设置大根堆: { 2 8 6 4 1 5 }
设置当前节点arr[0]=2为根节点
左节点索引: 1
右节点索引: 2
比较左节点arr[1]=8与根点节大小
比较右节点arr[2]=6与根点节大小
根节点小于子节点,交换根(arr[0]=2)与子(arr[1]=8)节点
{ 8 2 6 4 1 5 }
设置当前节点arr[1]=2为根节点
左节点索引: 3
右节点索引: 4
比较左节点arr[3]=4与根点节大小
比较右节点arr[4]=1与根点节大小
根节点小于子节点,交换根(arr[1]=2)与子(arr[3]=4)节点
{ 8 4 6 2 1 5 }
设置当前节点arr[3]=2为根节点
左节点索引: 7
右节点索引: 8
左节点超出范围,即不存在
右节点超出范围,即不存在
将最大值(arr[0]=8)与最后元素(arr[5]=5)交换
重新设置大根堆: { 5 4 6 2 1 }
设置当前节点arr[0]=5为根节点
左节点索引: 1
右节点索引: 2
比较左节点arr[1]=4与根点节大小
比较右节点arr[2]=6与根点节大小
根节点小于子节点,交换根(arr[0]=5)与子(arr[2]=6)节点
{ 6 4 5 2 1 }
设置当前节点arr[2]=5为根节点
左节点索引: 5
右节点索引: 6
左节点超出范围,即不存在
右节点超出范围,即不存在
将最大值(arr[0]=6)与最后元素(arr[4]=1)交换
重新设置大根堆: { 1 4 5 2 }
设置当前节点arr[0]=1为根节点
左节点索引: 1
右节点索引: 2
比较左节点arr[1]=4与根点节大小
比较右节点arr[2]=5与根点节大小
根节点小于子节点,交换根(arr[0]=1)与子(arr[2]=5)节点
{ 5 4 1 2 }
设置当前节点arr[2]=1为根节点
左节点索引: 5
右节点索引: 6
左节点超出范围,即不存在
右节点超出范围,即不存在
将最大值(arr[0]=5)与最后元素(arr[3]=2)交换
重新设置大根堆: { 2 4 1 }
设置当前节点arr[0]=2为根节点
左节点索引: 1
右节点索引: 2
比较左节点arr[1]=4与根点节大小
比较右节点arr[2]=1与根点节大小
根节点小于子节点,交换根(arr[0]=2)与子(arr[1]=4)节点
{ 4 2 1 }
设置当前节点arr[1]=2为根节点
左节点索引: 3
右节点索引: 4
左节点超出范围,即不存在
右节点超出范围,即不存在
将最大值(arr[0]=4)与最后元素(arr[2]=1)交换
重新设置大根堆: { 1 2 }
设置当前节点arr[0]=1为根节点
左节点索引: 1
右节点索引: 2
比较左节点arr[1]=2与根点节大小
右节点超出范围,即不存在
根节点小于子节点,交换根(arr[0]=1)与子(arr[1]=2)节点
{ 2 1 }
设置当前节点arr[1]=1为根节点
左节点索引: 3
右节点索引: 4
左节点超出范围,即不存在
右节点超出范围,即不存在
将最大值(arr[0]=2)与最后元素(arr[1]=1)交换
重新设置大根堆: { 1 }
设置当前节点arr[0]=1为根节点
左节点索引: 1
右节点索引: 2
左节点超出范围,即不存在
右节点超出范围,即不存在
排序后: { 1 2 4 5 6 8 9 23 39 66 }

以上就是C++ 大根堆排序学习笔记的详细内容,更多关于C++ 大根堆排序的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文