c++深入浅出讲解堆排序和堆

C++深入浅出讲解堆排序和堆

堆的定义

堆是一种特殊的树形数据结构,它满足以下两个特性:

  • 堆是一个完全二叉树(Complete Binary Tree);
  • 堆中每个节点的值都大于等于(或小于等于)其左右子节点的值。

可以看出,堆一般分为两种类型:大根堆(Max Heap)和小根堆(Min Heap)。大根堆的每个节点的值都大于等于其左右子节点的值,小根堆则相反。

堆的应用

堆主要应用于优先队列、Top K 问题、求中位数等场景。

  • 在优先队列中,堆可以快速地找到队列中的最大或最小值;
  • 在 Top K 问题中,堆可以维护一个大小为 K 的堆,依次将数据插入堆中,最后堆中的数据即为前 K 大或前 K 小的元素;
  • 求中位数问题可以通过维护两个堆来处理,使得左边堆的最大值小于右边堆的最小值。

堆的实现

堆可以通过数组来实现,因为完全二叉树可以被序列化成一个数组。

插入元素

插入元素时,可以将新元素插入到堆的最后一个位置,然后使用一个循环将新元素与其父节点进行比较,如果父节点的值小于新元素,则将父节点和新元素交换位置,直到新元素的父节点的值大于等于新元素的值为止。

删除元素

删除元素时,可以将堆中最后一个元素移到堆顶,然后让其与左右子节点中较大的值进行比较,如果较大的值大于该节点的值,则将其与较大的子节点交换位置,并继续向下比较。直到该节点的值大于等于其左右子节点的值为止。

堆排序

堆排序是一种不稳定的排序算法,它的时间复杂度为 O(nlogn)。

堆排序的思路

  1. 构建大根堆(或小根堆):将待排序数组构建成一个大根堆(或小根堆);
  2. 堆排序:将大根堆(或小根堆)根节点与最后一个叶子节点进行交换,并将交换后的最后一个叶子节点排除在堆的范围之外;此时,根节点可能不再满足堆的性质,因此需要将其与左右子节点中较大的节点进行比较并交换位置,然后继续向下比较,直到满足堆的性质为止;
  3. 重复步骤 2,直到整个数组有序。

堆排序的实现

void heapSort(vector<int>& nums) {
    int n = nums.size();
    // 从最后一个非叶子节点开始,依次向下调整堆
    for (int i = (n - 2) / 2; i >= 0; i--) {
        heapify(nums, i, n);
    }
    // 交换堆顶与最后一个叶子节点,并重新调整堆
    for (int i = n - 1; i > 0; i--) {
        swap(nums[0], nums[i]);
        heapify(nums, 0, i);
    }
}

void heapify(vector<int>& nums, int i, int n) {
    int largest = i;
    int left = i * 2 + 1, right = i * 2 + 2;
    if (left < n && nums[left] > nums[largest]) {
        largest = left;
    }
    if (right < n && nums[right] > nums[largest]) {
        largest = right;
    }
    if (largest != i) {
        swap(nums[i], nums[largest]);
        heapify(nums, largest, n);
    }
}

这里使用了一个辅助函数 heapify() 来调整堆,它将一个大小为 n 的数组 nums 中的第 i 个节点向下调整,使得以 i 为根节点的子树满足堆的性质。heapify() 的具体实现可以参考前面所述。

堆排序的示例

下面是一个堆排序的示例:

#include <iostream>
#include <vector>

using namespace std;

void heapify(vector<int>& nums, int i, int n);

void heapSort(vector<int>& nums) {
    int n = nums.size();
    // 从最后一个非叶子节点开始,依次向下调整堆
    for (int i = (n - 2) / 2; i >= 0; i--) {
        heapify(nums, i, n);
    }
    // 交换堆顶与最后一个叶子节点,并重新调整堆
    for (int i = n - 1; i > 0; i--) {
        swap(nums[0], nums[i]);
        heapify(nums, 0, i);
    }
}

void heapify(vector<int>& nums, int i, int n) {
    int largest = i;
    int left = i * 2 + 1, right = i * 2 + 2;
    if (left < n && nums[left] > nums[largest]) {
        largest = left;
    }
    if (right < n && nums[right] > nums[largest]) {
        largest = right;
    }
    if (largest != i) {
        swap(nums[i], nums[largest]);
        heapify(nums, largest, n);
    }
}

int main() {
    vector<int> nums = {3, 8, 5, 2, 9, 7, 1, 4, 6};
    heapSort(nums);
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

输出结果如下:

1 2 3 4 5 6 7 8 9

这个例子演示了如何使用堆排序将无序数组 nums 排序。经过堆排序之后,数组 nums 变成了升序排列。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:c++深入浅出讲解堆排序和堆 - Python技术站

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

相关文章

  • 数据排序谁最快(javascript中的Array.prototype.sort PK 快速排序)

    首先,我们需要明确两个概念:Array.prototype.sort 和 快速排序算法。 Array.prototype.sort() 是 JavaScript 数组原生的排序方法,可以用于将数组中的元素按照某种规则进行排序。而快速排序算法则是一种高效的排序算法,其核心思想是通过递归将数组拆分成多个小数组,然后依次对这些小数组进行排序。 Array.prot…

    算法与数据结构 2023年5月19日
    00
  • Golang实现四种负载均衡的算法(随机,轮询等)

    Golang实现四种负载均衡的算法(随机,轮询等) 负载均衡是指在分布式系统中,将工作负载分摊到多个计算资源来进行共同处理的技术。 Golang作为一种高性能、可靠性语言,天然适合做负载均衡,因此我们可以用Golang实现四种常用的负载均衡算法。 什么是负载均衡算法? 负载均衡算法是指在分发服务时,选择合适的服务器来处理请求的一种算法。负载均衡可分为静态负载…

    算法与数据结构 2023年5月19日
    00
  • PHP数组递归排序实现方法示例

    当我们需要对 PHP 数组进行排序时,通常会使用 sort() 或者 usort() 函数,但这些函数只能对一维数组进行排序。当数组是多维结构时,我们需要使用递归的方式进行实现。 以下是一个 PHP 数组递归排序的示例实现: 定义待排序的数组 $student_scores = [ "class 1" => [ "Pete…

    算法与数据结构 2023年5月19日
    00
  • PHP快速排序算法实现的原理及代码详解

    下面我就详细讲解一下“PHP快速排序算法实现的原理及代码详解”的完整攻略。 一、快速排序算法的原理 快速排序(Quicksort)是非常常用的排序算法,其基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的记录关键字小,然后分别对这两部分记录继续进行排序,重复上述过程,直到整个序列有序为止。 具体流程如下: 从数列中挑出一…

    算法与数据结构 2023年5月19日
    00
  • 手把手教你搞懂冒泡排序和选择排序

    手把手教你搞懂冒泡排序和选择排序 冒泡排序 冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换的数据为止。 算法流程 比较相邻的元素。如果当前的元素大于下一个元素,则交换它们的位置。 对每一对相邻元素都执行步骤 1,从开始第一对到…

    算法与数据结构 2023年5月19日
    00
  • C语言之直接插入排序算法的方法

    C语言直接插入排序算法的方法 什么是直接插入排序 直接插入排序,是一种应用最广泛的排序算法之一,也是一种稳定的排序算法。它的基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的有序表。具体的过程是将待排序的元素插入到已经排好序的元素中,使插入后仍保持有序。 代码实现 下面是用C语言实现直接插入排序算法的代码: void direct_insert…

    算法与数据结构 2023年5月19日
    00
  • javascript冒泡排序小结

    JavaScript冒泡排序小结 什么是冒泡排序 冒泡排序是一种经典排序算法,它重复地走访过要排序的数列,每次比较相邻的两个元素,如果顺序不对则交换它们,直到没有需要交换的元素为止。 冒泡排序的步骤 冒泡排序的主要步骤如下: 比较相邻的元素。如果第一个比第二个大,就交换它们; 对每一对相邻的元素做同样的工作,从开始的第一对到结尾的最后一对,这样在最后的元素应…

    算法与数据结构 2023年5月19日
    00
  • Java中自然排序和比较器排序详解

    Java中自然排序和比较器排序详解 简介 Java中排序分为自然排序和比较器排序两种方式。当对象包含了Comparable接口实现的compareTo方法时,便支持了自然排序。而比较器排序则需要自己实现一个Comparator接口,并传入调用方法中。本文将从以下几个方面详细介绍这两种排序方式: Comparable接口及compareTo方法 Compara…

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