【数据结构】堆(Heap)

news/2025/1/12 6:01:33/

文章目录

  • 前言
  • 一、堆
    • 1、 概念
    • 2、性质
    • 3、结构
  • 二、堆的实现
    • 1、算法实现:
      • 向下调整算法
      • 向上调整算法(堆的创建)
      • 堆的插入
      • 堆的删除
      • 堆的排序
    • 2、 代码实现(小堆):
      • 堆的定义
      • 交换
      • 检查容量
      • 向下调整
      • 向上调整
      • 堆的初始化
      • 堆的创建
      • 销毁堆
      • 堆的插入
      • 堆的删除
      • 获取堆顶元素
      • 判空
      • 堆排序
      • 打印堆

前言

基本概念:

1、完全二叉树:若二叉树的深度为h,则除第h层外,其他层的结点全部达到最大值,且第h层的所有结点都集中在左子树。

2、满二叉树:满二叉树是一种特殊的的完全二叉树,所有层的结点都是最大值。

一、堆

1、 概念

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<=K2i+2 ,则称为小堆(或大堆)。

将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

2、性质

  1. 堆中某个节点的值总是不大于或不小于其父节点的值;
  2. 堆总是一棵完全二叉树。

3、结构

在这里插入图片描述

二、堆的实现

1、算法实现:

向下调整算法

堆的向下调整:
(以小堆为例)

  • 先设定根节点为当前节点(通过下标获取,标记为cur),比较左右子树的值,找出更小的值,用child来标记。
  • 比较child和cur的值,如果child比cur小,则不满足小堆的规则,需要进行交换。
  • 如果child比cur大,满足小堆的规则,不需要交换,调整结束。
  • 处理完一个节点之后,从当前的child出发,循环之前的过程。
    向下调整(小堆)示例:
    在这里插入图片描述
    向下调整(大堆)示例:

在这里插入图片描述

向上调整算法(堆的创建)

下面我们给出两个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。

根节点左右子树不是堆,我们怎么调整呢?

这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

堆的向上调整:
(以小堆为例)

  • 先设定倒数的第一个叶子节点为当前节点(通过下标获取,标记为cur),找出他的父亲节点,用parent来标记。
  • 比较parent和cur的值,如果cur比parent小,则不满足小堆的规则,需要进行交换。
  • 如果cur比parent大,满足小堆的规则,不需要交换,调整结束。
  • 处理完一个节点之后,从当前的parent出发,循环之前的过程。
int a[] =    {9,7,8,10,3,6}

向上调整(小堆)示例:
在这里插入图片描述

int a[] =    {1,5,3,8,7,6}

向上调整(大堆)示例:
在这里插入图片描述

堆的插入

将数据插入到数组最后,再进行向上调整。

int a[]={5,10,15,20}
int a[]={5,10,15,20,4}

示例:
在这里插入图片描述

堆的删除

删除堆是删除堆顶的数据。

将堆顶的数据和最后一个数据交换,然后删除数组最后一个数据,再进行向下调整算法。

示例:
在这里插入图片描述

堆的排序

基本思想:

将待排序序列构造成一个大顶堆
此时,整个序列的最大值就是堆顶的根节点。
将其与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n-1个元素的次小值。如此反复执行,便能得到一个有序序列了。

动态演示:
可视化网站:数据结构和算法动态可视化

在这里插入图片描述

2、 代码实现(小堆):

(以小堆为示例)

堆的定义

typedef int HDataType;
typedef struct heap
{HDataType* data;int size;int capacity;
}heap;

交换

void Swap(int* a, int* b)
{int temp = *a;*a = *b;*b = temp;
}

检查容量

void checkCapcity(heap* hp)
{if (hp->capacity == hp->size){int newC = hp->capacity == 0 ? 1 : 2 * hp->capacity;hp->data = (HDataType*)realloc(hp->data, sizeof(HDataType)*newC);hp->capacity = newC;}
}

向下调整

void shiftDown(int* arr, int n, int cur)
{int child = 2 * cur + 1;while (child < n){//比较左右子树,找到较小值if (child + 1 < n &&arr[child + 1]<arr[child]){	++child;//child时刻保存较小值的下标}if (arr[child] < arr[cur]){Swap(&arr[child], &arr[cur]);cur = child;child = 2 * cur + 1;}else{break;}}
}

向上调整

void shiftUp(int* arr, int n, int cur)
{int parent = (cur - 1) / 2;while (cur > 0){if (arr[cur] < arr[parent]){Swap(&arr[cur], &arr[parent]);cur = parent;parent = (cur - 1) / 2;}else{break;}}}

堆的初始化

void heapInit(heap* hp)
{assert(hp);hp->data = NULL;hp->capacity = hp->size = 0;
}

堆的创建

void heapCreate(heap* hp, int* arr, int n)
{assert(hp);hp->data = (HDataType*)malloc(sizeof(HDataType)*n);memcpy(hp->data, arr, sizeof(HDataType)*n);hp->capacity = hp->size = n;for (int i = (n - 2) / 2; i >= 0; i--){shiftDown(hp->data, hp->size, i);}
}

销毁堆

void heapDestory(heap* hp)
{assert(hp);free(hp->data);hp->data = NULL;hp->capacity = hp->size = 0;
}

堆的插入

void heapPush(heap* hp, HDataType val)
{assert(hp);checkCapcity(hp);hp->data[hp->size++] = val;shiftUp(hp->data, hp->size, hp->size - 1);
}

堆的删除

void heapPop(heap* hp)
{if (hp->size > 0){swap(&hp->data[0], &hp->data[hp->size - 1]);hp->size--;shiftDown(hp->data, hp->size, 0);}
}

获取堆顶元素

HDataType heapTop(heap* hp)
{assert(hp);return hp->data[0];
}

判空

int heapEmpty(heap* hp)
{if (hp == NULL || hp->size == 0){return 1;}else{return 0;}
}

堆排序

void heapSort(heap* hp)
{assert(hp);for (int i = (hp->size - 2) / 2; i >= 0; i--){shiftDown(hp->data, hp->size, i);}int end = hp->size - 1;while (end > 0){Swap(&hp->data[0], &hp->data[end]);shiftDown(hp->data, end, 0); end--;}
}

打印堆

void HeapPrint(heap* hp)
{assert(hp);for (int i = 0; i < hp->size; i++){printf("%d ", hp->data[i]);}printf("\n");
}

http://www.ppmy.cn/news/80565.html

相关文章

java泛型初阶和包装类

文章目录 1 包装类6 泛型如何编译的6.1 擦除机制6.2 为什么不能实例化泛型类型数组 7 泛型的上界7.1 语法7.2 示例7.3 复杂示例 8 泛型方法8.1 定义语法8.2 示例8.3 使用示例-可以类型推导8.4 使用示例-不使用类型推导 1 包装类 在Java中&#xff0c;由于基本类型不是继承自Ob…

5.22作业

1.ref作用&#xff1a; 放到 dom 节点上 >> 获取原生 dom 组件身上 >> 获取组件实例 >> 可以获取组件内部所有的方法和数据 2. vue组件通信? 父传子 在⼦组件的标签上定义属性 ⼦组件通过props来进⾏接受,可以通过数组的⽅式进⾏接…

霍尔电流传感器在直流列头柜的应用

摘要&#xff1a;数据中心供电电源质量的好坏直接影响到IT设备的安全运行&#xff0c;因此对数据中心直流列头柜电源进出线实行监测非常重要&#xff0c;而通过霍尔电流传感器可以采集主进线电流、多路支路直流电流和漏电流。 关键词&#xff1a;数据中心&#xff1b;直流列头柜…

【周末闲谈】你知道物联网技术吗?

连接万物&#xff0c;创造未来。从智能家居到智慧医疗&#xff0c;从智能车联到智慧城市&#xff0c;物联网技术的影响已经悄然渗透到了我们的方方面面。欢迎大家积极讨论联网技术如何影响了我们的生活。 个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【…

ES(Elasticsearch)的docker安装部署教程

0、 服务器版本信息 Red Hat 4.8.5-44 CentOS Linux release 7.9.2009 (Core) 1、ES部署 1.1 拉取docker镜像 docker pull elasticsearch:7.10.1拉取成功的镜像&#xff0c;可以使用如下命令查看&#xff1a; docker images 上图2年之前表示该elasticsearch的7.10.1镜像版…

Python 爬虫(七):pyspider 使用

1 简介 pyspider 是一个支持任务监控、项目管理、多种数据库&#xff0c;具有 WebUI 的爬虫框架&#xff0c;它采用 Python 语言编写&#xff0c;分布式架构。详细特性如下&#xff1a; 拥有 Web 脚本编辑界面&#xff0c;任务监控器&#xff0c;项目管理器和结构查看器&#…

Solidity中的可支付函数是什么?

学习Solidity中可支付函数的相关知识&#xff0c;了解它们在处理以太币存款方面的重要性&#xff0c;以及如何在智能合约中创建和使用它们。 目标 通过本指南&#xff0c;您应该能够&#xff1a; 理解Solidity中可支付函数的目的和用法 学习如何向智能合约发送Ether 编写Solidi…

算法题3 — 求字符串中的最长子串

文章目录 题目示例示例1示例2示例3 解题解法1解法2 leetcode 题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 示例1 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 示例…