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语言实现飞机大战小游戏

    C语言实现飞机大战小游戏完整攻略 简介 飞机大战是一款经典的小游戏,它的玩法简单却精巧,是C语言初学者不错的练手项目。本文将详细介绍如何用C语言实现飞机大战小游戏。 准备工作 在开始编写游戏代码前,我们需要做一些准备工作: 安装开发环境(比如 Visual Studio Code,CodeBlocks 等等); 了解游戏窗口、控件绘制、键盘事件等基础知识。 …

    C 2023年5月22日
    00
  • C++中的extern “C”用法详解

    C++中的extern “C”用法详解 简介 在C++中,存在着C和C++的二进制兼容性问题,即C++编译后的函数名与C编译后的函数名不一样。这会导致当我们在头文件中声明一个C++函数的时候,在C语言中无法使用这个函数。所以我们需要在C++ 中使用 extern “C” 关键字声明特定函数,以便在 C++ 环境下使用 C 标准程序声明及定义的函数。 用法 使…

    C 2023年5月23日
    00
  • UltraEdit技巧总结

    UltraEdit 技巧总结攻略 简介 UltraEdit 是一款功能强大的文本编辑器,被广泛应用于程序员、系统管理员、DBA 等专业人群的日常工作中。UltraEdit 不仅仅是一个文本编辑器,还拥有丰富的编码、调试、FTP/SFTP 等功能。本文旨在总结 UltraEdit 的常见技巧,帮助使用者提高使用效率和体验。 使用技巧 以下是使用 UltraEd…

    C 2023年5月22日
    00
  • C++中的编译与链接

    C++中的编译与链接是将源代码转换为可执行文件的过程。它分为三个阶段:预处理、编译和链接。 预处理 预处理是C++编译过程的第一个阶段,该阶段将源文件中的预处理指令处理为有效的C++代码。 预处理器在编译之前会检查源文件并执行以下操作: 处理所有以 “#” 开头的预处理指令。 删除所有注释(// 和 / /)。 将所有 #include 指令替换为相应头文件…

    C 2023年5月23日
    00
  • PHP简洁函数(PHP简单明了函数语法)

    PHP简洁函数(PHP简单明了函数语法) PHP简洁函数是一种通过使用闭包函数创建匿名函数来减少不必要的代码和提高代码可读性的技术。它允许你在需要的地方定义函数同时避免使用全局变量和函数名冲突的问题。PHP简洁函数的语法非常简单明了,它的形式如下: $func = function($arg1, $arg2, …) { // function body …

    C 2023年5月22日
    00
  • 如何辨别htc真假 HTC手机真假辨别/htc鉴别翻新机详细攻略

    如何辨别HTC真假?——HTC手机真假辨别/HTC鉴别翻新机详细攻略 在购买HTC手机时,许多人都会遇到以下问题:如何辨别HTC手机的真假?如何判断购买的HTC手机是否是翻新机?本文将从多个方面为大家介绍HTC手机真假辨别及其详细攻略。 1. 查看HTC手机的包装 正品HTC手机的包装通常是印有HTC Logo和HTC名称的,图案清晰明了。一般来说,假冒手机…

    C 2023年5月22日
    00
  • C语言实现校园导游系统

    C语言实现校园导游系统攻略 1. 系统概述 本系统旨在实现校园导游功能,包括以下两个主要功能: 给出校园地图,包括景点名称、景点描述、景点图片等信息。 提供导游功能,可根据用户输入,为用户提供一条包含多个景点的导游路线,并展示每个景点的信息和图片。 本系统使用C语言实现。主要技术栈包括链表结构、图论算法、文件读写等。 2. 实现过程详解 2.1 数据存储 本…

    C 2023年5月23日
    00
  • C++ 中私有继承的作用

    C++ 中的私有继承是一种继承方式,它可以让派生类的对象获得基类的成员,但是这些成员只能在派生类内部访问,从而实现了封装。私有继承的作用有以下几点: 实现代码复用 私有继承可以实现代码的复用。比如有一个基类 Person,其中定义了一些通用的成员变量和函数,而派生类 Teacher 和 Student 都需要使用这些成员。此时可以通过私有继承来避免代码重复。…

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