C语言每日练习之二叉堆

C语言每日练习之二叉堆

什么是二叉堆?

二叉堆是一种特殊的二叉树,它满足两个特性:

  1. 堆的父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值;
  2. 堆总是一棵完全二叉树。

实现二叉堆

数据结构

为了实现二叉堆,我们需要先定义数据结构。二叉堆常常使用数组来表示,数组中第一个元素一般为根节点,其余元素依次为树中其它节点的值。通过数组下标计算节点间的关系,可以快速地访问堆中的元素。

typedef struct 
{
  int* array;     // 指向堆的数组
  int count;      // 堆中元素的数量
  int capacity;   // 堆的容量,即最多可以存储多少个元素
} Heap;

创建二叉堆

我们可以使用自上而下或自下而上两种方式创建二叉堆。这里我们讲下自下而上的方法,从最后一个非叶子节点开始。假设当前节点的下标是i,其父节点的下标为(i-1)/2,左右子节点的下标为2*i+12*i+2,那么可以用下面的代码创建一个二叉堆。

void buildHeap(Heap* heap, int* array, int count)
{
  // 将数组复制到堆中,注意下标从0开始
  memcpy(heap->array, array, count * sizeof(int));
  heap->count = count;
  heap->capacity = count;

  for (int i = (count - 1) / 2; i >= 0; i--)
  {
    shiftDown(heap, i);
  }
}

插入元素

在向堆中插入元素时,我们可以将其插到数组的尾部,并沿着其父节点和祖先节点逐步上移,以保持堆的特性。

void insert(Heap* heap, int data)
{
  assert(heap->count < heap->capacity);

  heap->array[heap->count] = data;
  heap->count++;

  int i = heap->count - 1;
  while (i > 0 && heap->array[(i - 1) / 2] < heap->array[i])
  {
    swap(&heap->array[(i - 1) / 2], &heap->array[i]);
    i = (i - 1) / 2;
  }
}

弹出堆顶元素

在弹出堆顶元素时,我们可以取出根节点,并将堆尾的元素放到根节点的位置,然后沿着其子节点逐步下移,以保持堆的特性。

int pop(Heap* heap)
{
  assert(heap->count > 0);

  int max_value = heap->array[0];
  heap->count--;

  heap->array[0] = heap->array[heap->count];
  shiftDown(heap, 0);

  return max_value;
}

下移元素

在下移元素时,我们可以将其与其两个子节点中较大的一个比较,如果小于子节点的值,则交换位置,继续向下比较。这样可以确保每次下移时,选择的都是当前节点和子节点中最大的那个,从而保证堆的特性不变。

void shiftDown(Heap* heap, int i)
{
  int max = i;
  int left = 2 * i + 1;
  int right = 2 * i + 2;

  if (left < heap->count && heap->array[left] > heap->array[max])
  {
    max = left;
  }

  if (right < heap->count && heap->array[right] > heap->array[max])
  {
    max = right;
  }

  if (max != i)
  {
    swap(&heap->array[i], &heap->array[max]);
    shiftDown(heap, max);
  }
}

示例说明

示例一:使用二叉堆来进行堆排序

假设我们有一个数组int array[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};,我们可以使用一个二叉堆来排序这个数组。首先将数组中的元素插入到二叉堆中,然后反复弹出堆顶元素。按照从大到小(或从小到大)的顺序,输出弹出的元素即为排好序的数组。

void heapSort(int* array, int count)
{
  Heap heap;
  heap.array = (int*)malloc(count * sizeof(int));
  heap.count = 0;
  heap.capacity = count;

  for (int i = 0; i < count; i++)
  {
    insert(&heap, array[i]);
  }

  for (int i = count - 1; i >= 0; i--)
  {
    array[i] = pop(&heap);
  }

  free(heap.array);
}

示例二:求一个数组中第k大的元素

假设我们有一个数组int array[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};,我们想要求出其中第5大的元素。我们可以使用一个小根堆来维护前k大的元素(假设我们维护的是前k大的元素,那么堆的容量为k),将数组中的元素逐个插入到堆中,如果堆中元素的数量大于k,就弹出堆顶元素。当插入完所有的元素后,堆顶元素即为第k大的元素。

int findKthLargest(int* array, int count, int k)
{
  Heap heap;
  heap.array = (int*)malloc(k * sizeof(int));
  heap.count = 0;
  heap.capacity = k;

  for (int i = 0; i < count; i++)
  {
    if (heap.count < k)
    {
      insert(&heap, array[i]);
    }
    else if (array[i] > heap.array[0])
    {
      heap.array[0] = array[i];
      shiftDown(&heap, 0);
    }
  }

  int result = heap.array[0];
  free(heap.array);

  return result;
}

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言每日练习之二叉堆 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • Docker 部署分布式搜索引擎 Elastic Search的详细过程

    下面我来为你详细讲解“Docker 部署分布式搜索引擎 Elastic Search的详细过程”。 什么是 Elastic Search Elastic Search 是一个分布式的、可扩展的全文搜索引擎,可以帮助我们快速地索引、搜索数据。它基于Lucene搜索引擎构建,提供了 RESTful API 接口,可以对数据进行复杂的搜索。 Docker 安装 E…

    other 2023年6月27日
    00
  • Vue数据更新视图不更新的几种解决方案小结

    下面就为大家详细讲解Vue数据更新视图不更新的几种解决方案小结。 一、问题描述 在使用Vue时,有时候我们会遇到数据更新了,但是视图没有更新的情况,这是因为Vue使用的是异步更新的方式,如果数据变化时视图没有立即响应,则应该考虑使用以下几种解决方案: 二、解决方案 方案一:使用this.$set强制更新响应式变量 Vue使用Object.definedPro…

    other 2023年6月27日
    00
  • QQ7.6(15685)体验版申请地址及更新官方下载

    QQ7.6(15685)体验版申请地址及更新官方下载攻略 QQ7.6(15685)体验版是腾讯公司最新发布的QQ版本,为了获得该版本并进行体验,您需要按照以下步骤进行操作。 1. 访问官方网站 首先,您需要访问腾讯官方网站以获取QQ7.6(15685)体验版的申请地址和更新官方下载链接。请在浏览器中输入以下网址: https://www.qq.com 2. …

    other 2023年8月3日
    00
  • 实例讲解避免javascript冲突的方法

    实例讲解避免 JavaScript 冲突的方法 在开发网页时,经常会遇到多个 JavaScript 库或框架同时使用的情况,这可能导致命名冲突和功能冲突。为了避免这些冲突,我们可以采取一些方法来确保 JavaScript 代码能够正确地运行。下面是两种常见的方法示例: 1. 使用命名空间 命名空间是一种将变量和函数封装在一个对象中的技术,以避免全局命名冲突。…

    other 2023年7月29日
    00
  • Kotlin协程Flow生命周期及异常处理浅析

    Kotlin协程Flow生命周期及异常处理浅析 什么是Kotlin协程Flow Kotlin协程Flow是一个异步数据流工具,可以在一段时间内(可能是无限)发出多个异步结果。我们可以通过Flow来实现类似RxJava的响应式流操作。Flow适用于需要异步处理数据流的业务场景。 Kotlin协程Flow的生命周期 Flow的生命周期由挂起函数的最后一个流操作符…

    other 2023年6月27日
    00
  • 自己搭建cdn服务器赚钱

    以下是详细的步骤和示例: 步骤1:选择CDN 首先,您需要选择一个CDN服务器。您可以选择一些知名的CDN服务提供商,如阿里云腾讯云、百度云等,也可以选择一些开源的CDN服务器,如Nginx、Varnish等。 步骤2:搭建CDN服务器 以下是使用Nginx搭建CDN服务器的示例 示例1:安装Nginx 首先,您需要安装Nginx。您可以使用以下命令在Ubu…

    other 2023年5月6日
    00
  • 用python实现批量重命名文件的代码

    当需要对大量的文件进行重命名时,手动逐个改名未免太过于低效。Python可以帮助我们实现批量重命名文件的操作。下面是具体的步骤: 1.导入os模块 在Python中,想要操作文件或目录,必须要导入os模块,因为os模块提供了很多文件及目录相关的操作函数。所以,开头的第一步就是导入os模块。 import os 2.使用os模块中的rename方法进行重命名 …

    other 2023年6月26日
    00
  • Android实现两个数相加功能

    Android实现两个数相加功能的完整攻略 步骤一:创建布局文件 首先,我们需要创建一个布局文件来显示用户界面。在res/layout目录下创建一个新的XML文件,例如activity_main.xml,并添加以下代码: <LinearLayout xmlns:android=\"http://schemas.android.com/apk/…

    other 2023年9月6日
    00
合作推广
合作推广
分享本页
返回顶部