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日

相关文章

  • 用C语言举例讲解数据结构中的算法复杂度结与顺序表

    让我来为你讲解“用C语言举例讲解数据结构中的算法复杂度结与顺序表”的完整攻略。具体如下: 一、算法复杂度 1.1 什么是算法复杂度 算法复杂度是衡量算法运行效率的重要指标。包括时间复杂度和空间复杂度。时间复杂度指算法解决问题所用的时间,通常用大O符号表示;空间复杂度指算法解决问题所需的内存空间大小。 1.2 如何分析算法复杂度 可以从以下三个方面来分析算法复…

    数据结构 2023年5月17日
    00
  • 「分治」黑白棋子的移动

    本题为3月23日23上半学期集训每日一题中A题的题解 题面 题目描述 有2n个棋子(n≥4)排成一行,开始位置为白子全部在左边,黑子全部在右边,如下图为n=5的情形: ○○○○○●●●●● 移动棋子的规则是:每次必须同时移动相邻的两个棋子,颜色不限,可以左移也可以右移到空位上去,但不能调换两个棋子的左右位置。每次移动必须跳过若干个棋子(不能平移),要求最后能…

    算法与数据结构 2023年4月18日
    00
  • Mysql Innodb存储引擎之索引与算法

    Mysql Innodb存储引擎之索引与算法 MySQL是一款非常受欢迎的关系型数据库,有许多的存储引擎可供选择,其中InnoDB是目前最受欢迎的存储引擎之一。索引是InnoDB存储引擎的一个重要特性,它可以大大提高数据库查询的效率。本文将详细讲解InnoDB存储引擎的索引与算法。 索引 索引是一种数据结构,它将表中的列与对应的行位置组成键值对,以便快速查找…

    数据结构 2023年5月17日
    00
  • python算法与数据结构朋友圈与水杯实验题分析实例

    让我来详细讲解一下“python算法与数据结构朋友圈与水杯实验题分析实例”的完整攻略。 1. 前言 本文将分享两个Python的算法与数据结构问题,即朋友圈和水杯实验题。我们将分别介绍问题的背景、解题思路和代码实现。 2. 朋友圈问题 2.1 背景 给定一个M*N的矩阵,矩阵中的每个元素都是1或0。如果矩阵中的1元素相邻,即水平、垂直或对角线相邻,则将这些元…

    数据结构 2023年5月17日
    00
  • C语言超详细讲解双向带头循环链表

    C语言双向带头循环链表 基本概念 带头双向循环链表是指在双向循环链表的基础上,在头节点前面添加一个头结点。这个头结点不存储任何数据,只是为了方便对链表进行操作。循环链表则是在单向或双向链表的基础上,使链表的头节点与尾节点相连,形成一个环。 综合这两种链表,就构成了“双向带头循环链表”这种数据结构。双向带头循环链表是一种灵活性较高的数据结构,支持前插、后插、前…

    数据结构 2023年5月17日
    00
  • 一行python实现树形结构的方法

    想要一行Python实现树形结构,我们需要使用Python的字典数据类型来完成任务。下面是详细的操作步骤: 创建树形结构字典 我们可以用嵌套字典来表示树形结构,我们需要选择其中一个节点作为根节点,并以键值对的形式保存其子节点。最终,我们将根节点作为整个字典的返回值。下面是实现代码: tree = lambda: defaultdict(tree) 插入节点 …

    数据结构 2023年5月17日
    00
  • C语言实题讲解快速掌握单链表下

    C语言实题讲解快速掌握单链表下 简介 单链表是常见的一种数据结构,可以存储任意数量的数据,并且可以高效的进行插入、删除和查找操作。本篇文章将介绍如何使用C语言实现单链表,以及如何应对在实现单链表时所遇到的常见问题。 实现过程 数据结构设计 为了实现单链表,我们需要设计一个数据结构来存储节点信息,一般包含两个成员,一个是数据域,用来存储实际的数据,另一个是指针…

    数据结构 2023年5月17日
    00
  • C语言编程数据结构基础详解小白篇

    C语言编程数据结构基础详解小白篇攻略 1. 确定学习目标 在学习过程中,需要明确学习目标。对于小白来说,首先要了解C语言的基本语法,同时也需要掌握常用的数据结构。 2. 学习基本语法 2.1 变量和数据类型 C语言的变量必须先定义后使用 常用的数据类型包括整型、字符型、浮点型等 2.2 控制流程 C语言中常用的控制流程包括条件语句和循环语句 条件语句包括if…

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