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日

相关文章

  • 三星C480FW打印机出现脱机问题怎么复位?

    三星C480FW打印机出现脱机问题如何复位? 如果你的三星C480FW打印机出现了脱机(Offline)问题,这可能是由于打印机连接的USB或无线网络中的问题导致。以下是复位打印机的步骤: 1. 确认网络连接 首先,你需要确保打印机已经正确连接到网络,并且网络连接是可靠的。 网络打印机 如果你的三星C480FW打印机是连接到网络的,你可以按照以下步骤来确保打…

    C 2023年5月23日
    00
  • C语言实现逆序输出详细

    当我们需要逆序输出一个字符数组或字符串时,我们可以使用C语言中的循环语句和数组下标实现。 首先,我们先定义一个字符数组或字符串,存储数据。接着,我们创建一个循环语句,从最后一个元素开始逆序输出到第一个元素。最后,我们在每个元素之间添加一个空格或其他特定符号,以便于人类阅读。 以下是完整的C语言实现逆序输出的攻略: 步骤如下: 1. 定义字符数组或字符串 我们…

    C 2023年5月23日
    00
  • C++实现学生信息管理系统(完整版)

    C++实现学生信息管理系统(完整版)攻略 准备工作 首先,在开始编写C++代码前,需要先配置好C++编译环境,比如Visual Studio或者Code::Blocks等等。 第二,我们需要了解一些基本的C++语法,比如变量、数据类型、函数等等。 实现步骤 步骤一:设计数据结构 在开始编写实现学生信息管理系统的程序之前,需要首先设计好数据结构。这里我们考虑使…

    C 2023年5月24日
    00
  • CMakeList中自动编译protobuf文件过程

    当使用Protobuf数据交换格式时,我们需要将.proto文件编译为相应的C++类才能在代码中使用它们。CMake是常用的构建工具之一,它具有内置的支持来自动生成Protobuf源代码。 以下是在CMakeList中自动编译protobuf文件的完整攻略: 步骤 1:从Google官网下载Protobuf 要在CMakeList中自动编译protobuf文…

    C 2023年5月23日
    00
  • C程序 将一个数组的所有元素复制到另一个数组

    下面我来详细讲解如何编写一份 C 程序来将一个数组的所有元素复制到另一个数组。 问题描述 假设有两个整型数组 arr1 和 arr2,现在的任务是将 arr1 的所有元素复制到 arr2 中。 思路分析 这个问题可以通过创建一个循环来实现,遍历 arr1 的所有元素并将其逐个复制到 arr2 中。因此,我们将创建一个 for 循环,并在循环中执行一个赋值操作…

    C 2023年5月9日
    00
  • 最短时间学会基于C++实现DFS深度优先搜索

    最短时间学会基于C++实现DFS深度优先搜索攻略 什么是DFS深度优先搜索 DFS即深度优先搜索,是一种基于搜索算法的遍历和检索树或图数据结构的算法。DFS算法采用深度优先策略,从根结点出发访问所有可达结点,直到叶子节点。在访问某个结点时,先访问该结点的第一个未访问的相邻节点,然后递归的访问其非相邻节点。其搜索的核心思想是根据某个搜索方向向前搜索到底,直至无…

    C 2023年5月22日
    00
  • C++实现扫雷小游戏(控制台版)

    以下是“C++实现扫雷小游戏(控制台版)”的完整攻略: 1. 确定游戏规则 在实现扫雷游戏前,需要确定游戏的具体规则,包括雷区大小、雷数、标记雷的方式以及游戏胜利条件等。通常一个雷区是由若干个格子组成,每个格子可能包含地雷,也可能不包含地雷,游戏胜利条件可以是找到所有没有地雷的格子,或者是正确标记了所有地雷的位置。 2. 编写程序 在明确游戏规则后,可以开始…

    C 2023年5月23日
    00
  • C++你最好不要做的几点小结

    以下是“C++你最好不要做的几点小结”的完整攻略。 C++你最好不要做的几点小结 1. 不要忘记初始化 C++中未初始化的变量是具有未定义值的,如果试图使用未初始化的变量,将会导致不可预知的结果。因此,在使用变量之前,一定要初始化。对于内建类型,可以使用默认值进行初始化,例如: int a = 0; // 将a初始化为0 bool b = false; //…

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