C语言实现堆的简单操作的示例代码

C语言实现堆的简单操作的示例代码

堆的定义

堆是指通过比较之后使得数组满足大/小根堆性质的一种近似完全二叉树结构。

堆的结构

堆有两种类型,分别为大根堆和小根堆。大根堆指所有父结点都大于等于其子结点,小根堆则相反,所有父结点都小于等于其子结点。

假设i为当前结点,那么其父结点为(i-1)/2,左子结点为(2i+1),右子结点为(2i+2)。

堆支持如下操作:

  1. 将一个元素加入到堆中

  2. 返回堆中最小/大值

  3. 删除堆中最小/大值

堆的实现

要实现一个堆,需要用到下面几个重要的函数:

  • MaxHeapify:将堆的某一个非叶子结点与子结点进行比较,从而得到大根堆。而对于小根堆,则是将其变为小根堆。

  • BuildMaxHeap:建立堆,从无序的数组开始建立一个大根堆/小根堆

  • HeapSort:对无序数组进行排序,由于最大/小值在根结点中,先将根结点(最大/小值)与最后一位进行交换,然后将除最后一位的剩余数组调整为大根堆

  • Insert:插入一个元素,插到堆的尾部,调用_sift_up_ 负责新元素的上移操作

  • DeleteMax:移除最大元素。将堆顶元素及末尾元素交换,移除末尾元素,再调用_sift_down_ 负责新顶点的下移操作。

  • Heapify:将任一数组变成堆。

下面我们通过代码实现堆的基本操作:插入和删除最大值

插入操作

void insert(int arr[], int n, int value){
    arr[n] = value;  // 新元素插在末尾
    int i = n;
    while(i > 0 && arr[(i-1)/2] < arr[i]){  // 如果新元素比父结点大,与父结点交换
        swap(arr[i], arr[(i-1)/2]);
        i = (i-1)/2;
    }
}

在这个函数中,首先将新元素插在末尾,然后使用while循环,循环条件保证了当前结点的父结点存在且其值小于当前结点的值,最后交换两者。这样的操作被称为_sift-up_,因为新元素已经插入到堆中,我们需要将其“上移”以保证堆的性质。

删除最大值

int remove(int arr[], int n){
    // 先将堆顶和堆底交换
    int root_value = arr[0];
    arr[0] = arr[n-1];
    n--;
    // 然后对堆顶执行_sift-down_
    int i = 0;
    while(i < n){
        int max_value = arr[i];
        int max_index = i;
        // 发现左子结点比当前结点大
        if(2*i + 1 < n && arr[2*i + 1] > max_value){
            max_value = arr[2*i + 1];
            max_index = 2*i + 1;
        }
        // 发现右子结点比当前结点大
        if(2*i + 2 < n && arr[2*i + 2] > max_value){
            max_value = arr[2*i + 2];
            max_index = 2*i + 2;
        }
        // 堆的性质被满足,处理结束
        if(max_index == i){
            break;
        }
        swap(arr[max_index], arr[i]);
        i = max_index;
    }
    return root_value;
}

这个函数首先将堆顶(即最大值)与堆底(最右边值)进行交换。接着执行_sift-down_ 操作,将新的根结点下放到其应被放置的位置使得堆的性质被满足。这里需要不断地获取左右结点,并交换以满足堆的性质。这种方式被称为“sift-down”。

示例说明

以大根堆为例,给出以下无序数组:

int arr[] = {10, 20, 15, 40, 50, 30};
int n = sizeof(arr)/sizeof(arr[0]);

我们来分别看看其插入和删除最大值之后的操作:

插入操作

如果在这个堆中插入元素60:

insert(arr, n, 60);

那么堆就变成:

60, 20, 15, 10, 50, 30, 40
删除最大值操作

如果我们先执行一次insert(arr, n, 60)函数,然后再删除其中元素60:

int root_value = remove(arr, n);

那么堆就变成:

50, 20, 15, 10, 40, 30

最大值被返回并从本堆中删除。

总结

通过上述示例,我们可以清楚地看到添加和删除堆元素的操作及其具体实现。同时,我们还可以使用大根堆和小根堆对数组进行排序,以提高算法效率。可以看出,堆是一种相对简单而强大的数据结构。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言实现堆的简单操作的示例代码 - Python技术站

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

相关文章

  • C++基础之this指针与另一种“多态”

    C++基础之this指针与另一种“多态” 1. this指针是什么? 在C++中,this指针有一个特殊的用途,它指向当前对象的指针。我们通常使用this指针来访问当前对象的成员变量和成员函数。 class Person { private: string name; public: Person(string name) { this->name =…

    C 2023年5月22日
    00
  • C语言实现维吉尼亚密码的示例代码

    本文将介绍如何使用C语言实现维吉尼亚密码,并提供示例代码和对代码的详细解释。 什么是维吉尼亚密码? 维吉尼亚密码是一种多表替换密码,具有很高的安全性。它通过多次替换明文中的每个字符来生成密文,替换规则基于密钥和一组密文表,因此需要人工进行密钥分配和密文表的生成。由于密钥和密文表不会在通信中传输,因此维吉尼亚密码非常安全。 维吉尼亚密码的实现方式 维吉尼亚密码…

    C 2023年5月24日
    00
  • js数组与字符串常用方法总结

    JS数组与字符串常用方法总结 本篇攻略主要介绍 JavaScript 中数组和字符串的常用方法。 数组 1. 创建数组 数组可以通过以下方式进行创建: var arr1 = []; // 空数组 var arr2 = new Array(); // 空数组 var arr3 = [1, 2, 3]; // 带有元素的数组 2. 数组的常用方法 2.1 pus…

    C 2023年5月22日
    00
  • c++ 让程序开机自动启动的方法

    当我们想让编写的c++程序自动启动时,可以采用下面几种方法来实现。 方法一:修改注册表 假设我们要设置的程序名为 test.exe,要将其设置为系统开机启动的程序。可以使用以下步骤: 打开注册表编辑器:在开始菜单中输入 regedit,打开注册表编辑器。 找到启动项:依次展开 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft…

    C 2023年5月23日
    00
  • C语言实现稀疏矩阵

    C语言实现稀疏矩阵的完整攻略 1、什么是稀疏矩阵? 稀疏矩阵是矩阵中绝大部分元素为0的矩阵。相对于密集矩阵,稀疏矩阵可以用更少的存储空间来存储矩阵中的数据。 2、如何实现稀疏矩阵? 2.1 稀疏矩阵的三元组存储法 稀疏矩阵的三元组存储法是最常用的矩阵存储方法之一。其基本思路是:将矩阵中的非零元素及其对应的行列下标存储起来,对于未存储的元素,默认其值为0。具体…

    C 2023年5月23日
    00
  • C语言使用setjmp和longjmp实现一个简单的协程

    下面是C语言使用setjmp和longjmp实现一个简单的协程的完整攻略。 什么是协程 协程是一种并发编程模型,可以看做是一种用户空间的轻量级线程。协程特点是占用资源少,切换代价低,不需要线程上下文切换的开销,仅通过自己写的切换机制进行上下文切换。由于协程不需要访问操作系统资源,因此基本不会发生阻塞的现象,其在I/O密集型任务中具有很好的应用前景。 使用se…

    C 2023年5月24日
    00
  • 一文带你深入了解C++中的类型转换

    一文带你深入了解C++中的类型转换 在C++中,类型转换是一种将一种数据类型转换为另一种数据类型的方法。类型转换在编程中非常常见,它可以将我们需要的数据类型作为参数传递给函数或表达式,也可以帮助我们处理特定的数据类型。 类型转换的分类 在C++中,类型转换可以分为隐式类型转换和显式类型转换两种: 隐式类型转换:自动将一种数据类型转换为另一种数据类型。例如,将…

    C 2023年5月24日
    00
  • 浅谈c++ vector和map的遍历和删除对象

    浅谈c++ vector和map的遍历和删除对象 概述 在c++的stl中,vector和map是常用的数据结构。它们都有遍历和删除对象的需求,下面将详细介绍如何使用c++ vector和map完成遍历和删除对象的操作。 vector的遍历和删除元素 遍历vector 遍历vector可以使用迭代器,得到vector的每个元素。 #include <i…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部