C++数据结构之堆详解

C++数据结构之堆详解

什么是堆

堆是一种完全二叉树。

堆分为大根堆和小根堆,大根堆满足每个节点的值都大于等于它的子节点,小根堆满足每个节点的值都小于等于它的子节点。

堆的实现

常见的实现堆的方式有数组和链表两种。

数组

由于二叉堆是完全二叉树,所以可以用数组来实现:

对于一个节点i,它的左子节点的下标是2 * i + 1,右子节点的下标是2 * i + 2,它的父节点的下标是(i - 1) / 2。

例如:

           1
         /  \
        2    3
       / \  / \
      4  5 6   7 

用数组实现时,可以按层级顺序将节点存储在数组中,下标为i的节点的左子节点为2 * i + 1,右子节点为2 * i + 2,其父节点为(i - 1) / 2。

上述二叉树可以用数组实现如下:

[1, 2, 3, 4, 5, 6, 7]

数组的第一个元素永远是根节点。

链表

堆也可以用链表来实现,每个节点包含指向左右子节点的指针和节点的值。

堆的基本操作

插入元素

要在堆中插入元素,必须保持堆的连通性,以及满足堆的特性。在插入一个新节点时,先插入到堆的最后一个位置,然后从该节点开始向上比较,如果该节点的值大于它的父节点的值,就交换它们的位置。

例如,现在有以下的大根堆:

          4
        /   \
       3     2
      / \   /
     1   5 6

要在这个堆中插入一个元素7,首先将它插入到堆的最后一个位置:

          4
        /   \
       3     2
      / \   / \
     1   5 6   7

然后从7节点开始向上比较,发现它大于2,所以将它与2交换位置:

          4
        /   \
       3     7
      / \   / \
     1   5 6   2

再与6交换位置:

          4
        /   \
       3     7
      / \   / \
     1   5 6   2

最终变成以下堆:

          7
        /   \
       3     6
      / \   / \
     1   5 4   2

删除元素

要在堆中删除一个元素,只能删除堆的根节点。将根节点移除后,将堆的最后一个元素放到根节点的位置,然后从该节点开始向下比较,如果该节点的值小于它的子节点的值,就交换它们的位置。

例如,现在有以下的大根堆:

          7
        /   \
       3     6
      / \   / \
     1   5 4   2

要从这个堆中删除最大的元素7,首先将它移除,并将堆的最后一个元素2放到根节点的位置:

          2
        /   \
       3     6
      / \   / 
     1   5 4   

然后从2节点开始向下比较,发现它小于6,所以将它与6交换位置:

          6
        /   \
       3     2
      / \   / 
     1   5 4   

再交换3和6的位置:

          6
        /   \
       1     2
      / \   / 
     3   5 4   

最终变成以下堆:

          6
        /   \
       1     4
      / \   / 
     3   5 2   

堆排序

堆排序使用堆的特性来排序元素。首先将所有元素插入到堆中,然后从堆中依次取出根节点,就可以得到一个有序序列。

例如,要对以下数组排序:

[3, 2, 5, 1, 4]

首先将它们插入到堆中:

          5
        /   \
       3     4
      / \   / 
     1   2   

然后依次从堆中取出根节点,就可以得到一个有序序列:

1, 2, 3, 4, 5

示例说明

示例1

以下是一个从小到大的堆排序示例:

#include <iostream>
#include <vector>

// 堆排序
void heap_sort(std::vector<int>& nums)
{
    // 构建初始堆
    int n = nums.size();
    for (int i = n / 2 - 1; i >= 0; i--)
        heapify(nums, n, i);

    // 依次取出堆顶元素
    for (int i = n - 1; i >= 0; i--)
    {
        std::swap(nums[0], nums[i]);
        heapify(nums, i, 0);
    }
}

// 堆排序的调整函数
void heapify(std::vector<int>& nums, int n, int i)
{
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    if (left < n && nums[left] > nums[largest])
        largest = left;

    if (right < n && nums[right] > nums[largest])
        largest = right;

    if (largest != i)
    {
        std::swap(nums[i], nums[largest]);
        heapify(nums, n, largest);
    }
}

int main()
{
    std::vector<int> nums{ 3, 2, 5, 1, 4 };

    heap_sort(nums);

    for (auto num : nums)
        std::cout << num << " ";

    return 0;
}

输出结果:

1 2 3 4 5

示例2

以下是一个从大到小的堆排序示例:

#include <iostream>
#include <vector>

// 堆排序
void heap_sort(std::vector<int>& nums)
{
    // 构建初始堆
    int n = nums.size();
    for (int i = n / 2 - 1; i >= 0; i--)
        heapify(nums, n, i);

    // 依次取出堆顶元素
    for (int i = n - 1; i >= 0; i--)
    {
        std::swap(nums[0], nums[i]);
        heapify(nums, i, 0);
    }
}

// 堆排序的调整函数
void heapify(std::vector<int>& nums, int n, int i)
{
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    if (left < n && nums[left] < nums[largest])
        largest = left;

    if (right < n && nums[right] < nums[largest])
        largest = right;

    if (largest != i)
    {
        std::swap(nums[i], nums[largest]);
        heapify(nums, n, largest);
    }
}

int main()
{
    std::vector<int> nums{ 3, 2, 5, 1, 4 };

    heap_sort(nums);

    for (auto num : nums)
        std::cout << num << " ";

    return 0;
}

输出结果:

5 4 3 2 1

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++数据结构之堆详解 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • [paper reading]|IC-FPS: Instance-Centroid Faster Point Sampling Module for 3D Point-base

    摘要: 本文说首次实现了大规模点云场景中基于点的模型的实时检测(<30ms); 首先指出FPS采样策略进行下采样是耗时的,尤其当点云增加的时候,计算量和推理时间快速增加; 本文提出IC-FPS;包含两个模块:local feature diffusion based background point filter (LFDBF);Centroid In…

    算法与数据结构 2023年4月17日
    00
  • MySQL数据库体系架构详情

    MySQL数据库体系架构是MySQL数据库自身的发展和演变过程中逐渐形成的一个庞大的体系。这个体系由多个组件构成,包括连接器、查询缓存、解析器、优化器、执行器、存储引擎等多个部分,其中存储引擎是其中最具有代表性的组件之一。在这篇攻略中,我们将详细讲解MySQL数据库体系架构的各个部分,介绍它们各自的功能和作用。 连接器 MySQL的连接器负责与客户端建立连接…

    数据结构 2023年5月17日
    00
  • C++ 超详细分析数据结构中的时间复杂度

    C++ 超详细分析数据结构中的时间复杂度攻略 什么是时间复杂度? 时间复杂度是用来衡量算法效率的指标,它表示的是算法的执行时间与问题规模之间的关系,通常用大O记法来表示。 如何分析时间复杂度? 1. 常见时间复杂度 以下是常见的时间复杂度及其对应的执行次数: 时间复杂度 对应执行次数 O(1) 常数级别 O(log n) 对数级别 O(n) 线性级别 O(n…

    数据结构 2023年5月17日
    00
  • Java数据结构之单链表详解

    下面是单链表攻略的详细讲解。 什么是单链表? 单链表是一种线性数据结构,它由一系列结点组成,每个结点包含数据域和指针域。数据域用于存储数据,指针域用于指向下一个结点。单链表的优点是插入和删除操作的时间复杂度为O(1),缺点是随机访问的时间复杂度为O(n)。 单链表的基本操作 单链表的基本操作包括插入操作、删除操作、查找操作和遍历操作。下面将分别介绍这些操作。…

    数据结构 2023年5月17日
    00
  • Java数据结构之二叉排序树的实现

    Java数据结构之二叉排序树的实现 二叉排序树(Binary Sort Tree)是一种特殊的二叉树结构,它的每个结点都包含一个关键字,并满足以下性质: 左子树中所有结点的关键字都小于根结点的关键字; 右子树中所有结点的关键字都大于根结点的关键字; 左右子树也分别为二叉排序树。 这种结构有助于实现快速的查找、插入和删除操作。在此,我们将展示一种实现二叉排序树…

    数据结构 2023年5月17日
    00
  • 字符串算法–$\mathcal{KMP,Trie}$树

    \(\mathcal{KMP算法}\) 实际上,完全没必要从\(S\)的每一个字符开始,暴力穷举每一种情况,\(Knuth、Morris\)和\(Pratt\)对该算法进行了改进,称为 \(KMP\) 算法。 而\(KMP\)的精髓在于,对于每次失配之后,我都不会从头重新开始枚举,而是根据我已经得知的数据,从某个特定的位置开始匹配;而对于模式串的每一位,都有…

    算法与数据结构 2023年4月18日
    00
  • C语言详细分析结构体的内存对齐规则

    C语言详细分析结构体的内存对齐规则 1. 什么是内存对齐 在计算机内存中,每个数据都需要分配一定的内存空间存储,这些空间的大小不一定相同。内存对齐就是要求每个数据按照某个规则,分配其所需的内存空间。 在C语言中,结构体是一种复合数据类型,由多个数据成员组成。结构体的数据成员排列顺序、数据类型均可能不同,因此需要内存对齐来规定内存空间的分配。 2. C语言中结…

    数据结构 2023年5月17日
    00
  • JoshChen_php新手进阶高手不可或缺的规范介绍

    JoshChen_php新手进阶高手不可或缺的规范介绍 作为一名PHP程序员,熟练掌握编程语言的同时,规范的代码风格也是不可或缺的。本文将介绍一些PHP规范的相关内容,帮助PHP新手进阶为高手。 1. 代码风格规范 1.1. 缩进 在编写代码时,缩进是非常重要的。按照规范,我们应该在每个缩进级别使用4个空格。 1.2. 命名规范 在PHP中,我们应该遵循以下…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部