堆/堆排序(C/C++)

        本篇文章将会较为全面的介绍堆的概念以及实现堆两个重要算法:向上调整算法和向下调整算法。接着实现了堆排序。

        若想查看对应位置,可直接按照以下目录进行查看:

        目录

1.堆的概念及结构

2.堆的实现

2.1 堆的向上调整算法

2.2 堆的向下调整算法

2.3 堆的插入

2.4 堆的删除 

2.5 堆的实现

3.堆排序

1.堆的概念及结构

        概念:若一个关键码的集合K=\left\{ k_0,k_1,k_2,k_3,...,k_{n-1} \right\},把它的所有元素按照完全二叉树的顺序存储方式存储在一个一维数组中,并满足:sss且xxx(xxx且zzzz),则称为小堆(大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

        性质:1.堆中的某个节点的值总是不大于或不小于其父节点的值;

                   2.堆总是一颗完全二叉树;

                   3.堆中父节点的关系与孩子节点的关系为:leftchild_k=parent_{2\times i+1},rightchild_k=parent_{2\times i+2}\,\,

        堆的结构如下:

        堆的存储结构:在数据结构中,对于二叉树的存储结构一般可以分为链式存储、顺序表存储;同样,对于堆的存储我们也可以选择链式存储和顺序表存储,但我们需要从中选出最优的存储结构。对于满二叉树和完全的二叉树的顺序表存储结构对于整个顺序表都可以将其存储满,并且下标的变化便于我们在堆中添加数据调整数据。所以我们使用顺序表来存储堆。如下:

2.堆的实现

        堆的实现主要的两个点为堆的建立和堆的调整,其中的主要思想为堆的向上调整算法向下调整算法,具体的实现将会在下面实现。

2.1 堆的向上调整算法

        堆的向上调整发算法是实现堆的插入核心算法。

        对于堆的建立,假设我们将一组数据一个一个的加入到堆中,那么我们需要在每一个元素入堆时都要进行调整,我们将加入的元素存入到顺序表的最后,然后利用双亲结点和孩子结点之间的关系来判断是否需要调整,知道调整到根节点为止。对于此算法思想既可以使用递归算法,也可以使用循环算法,在这里我们使用循环算法,如下:

        具体的实现算法如下:

void Swap(HPDataType* p1, HPDataType* p2) {HPDataType* tmp = *p1;*p1 = *p2;*p2 = tmp;
}void AdjustUp(HPDataType* a, int child) {int parent = (child - 1) / 2;while (child > 0) {//建立小堆,若建大堆,将 < 改为 >if (a[child] < a[parent]) {Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else {break;}}
}

2.2 堆的向下调整算法

        向下调整算法是堆删除堆顶元素的核心算法。

        向下调整算法的主要作用为:当我们需要将堆顶的元素弹出时,将整个堆进行维护,建立一个新的堆。其主要思路是将当前元素的孩子结点与双亲结点进行比较,但是此时双亲节点的孩子节点有两个,我们需要找出最合适的那一个,比如:建小堆则找出最小的孩子结点,建大堆则找出最大的孩子结点。然后将孩子结点与双亲结点进行交换,以此迭代,直到迭代到叶子结点。

        图示如下:

        以建小堆的算法为例,算法如下:

//建小堆的向下调整算法
void AdjustDown(HPDataType* a, int size, int parent) {//左孩子结点与双亲结点的关系int child = parent * 2 + 1;//假若右孩子比左孩子更小,则将child变量加一。child + 1 <size 用于保证不会越界//若建大堆,改为if ((a[child + 1] > a[child]) && (child + 1) < size)if ((a[child + 1] < a[child]) && (child + 1) < size) {++child;}//当child>=size时表示已经到达叶子结点跳出循环while (child<size) {//双亲结点大于孩子结点交换//若建大堆,改为if (a[parent] < a[child])if (a[parent] > a[child]) {Swap(&a[parent], &a[child]);//进行迭代parent = child;child = parent * 2 + 1;if ((a[child + 1] < a[child]) && (child + 1) < size) {++child;}}//若双亲结点不在大于孩子结点,说明已经迭代结束,不在需要继续进行迭代else {break;}}
}

2.3 堆的插入

        堆的插入就是在堆中插入元素,直接在堆的顺序存储的最后一个位置插入元素,然后调用向上调整算法,对堆进行维护。具体算法如下:

void HeapPush(Heap* php, HPDataType x) {assert(php);//判断顺序表的容量,是否需要扩容if (php->capacity == php->size) {int newCapacity = php->capacity == 0 ? 4 : 2 * (php->capacity);HPDataType* tmp = (HPDataType*)realloc(php->a,newCapacity * sizeof(HPDataType));if (tmp == NULL) {perror("realloc failed:");exit(1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;//调用向上调整算法AdjustUp(php->a, php->size);php->size++;
}

2.4 堆的删除 

        堆的删除就是将堆顶的元素弹出,然后使用向下调整算法对堆进行维护。具体算法如下:

void HeapPop(Heap* php) {assert(php);assert(php->a > 0);//将最后一个元素调换在堆顶元素位置,便于进行向下调整算法php->a[0] = php->a[php->size - 1];php->size--;//调用向下调整算法AdjustDown(php->a, php->size, 0);
}

2.5 堆的实现

        以下为堆的所有代码:

        Heap.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>typedef int HPDataType;typedef struct Heap {HPDataType* a;int size;int capacity;
}Heap;void HeapInit(Heap* php);
void HeapDestroy(Heap* php);
void HeapPush(Heap* php, HPDataType x);
void HeapPop(Heap* php);
HPDataType HeapTop(Heap* php);
int HeapSize(Heap* php);
bool HeapEmpty(Heap* php);void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int size, int parent);
void Swap(HPDataType* p1, HPDataType* p2);

        Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"void HeapInit(Heap* php) {assert(php);php->a = NULL;php->capacity = 0;php->size = 0;
}void HeapDestroy(Heap* php) {assert(php);free(php->a);php->capacity = php->size = 0;
}void Swap(HPDataType* p1, HPDataType* p2) {HPDataType* tmp = *p1;*p1 = *p2;*p2 = tmp;
}void AdjustUp(HPDataType* a, int child) {int parent = (child - 1) / 2;while (child > 0) {//建立小堆if (a[child] < a[parent]) {Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else {break;}}
}void HeapPush(Heap* php, HPDataType x) {assert(php);if (php->capacity == php->size) {int newCapacity = php->capacity == 0 ? 4 : 2 * (php->capacity);HPDataType* tmp = (HPDataType*)realloc(php->a,newCapacity * sizeof(HPDataType));if (tmp == NULL) {perror("realloc failed:");exit(1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;AdjustUp(php->a, php->size);php->size++;
}//建小堆的向下调整算法
void AdjustDown(HPDataType* a, int size, int parent) {//左孩子结点与双亲结点的关系int child = parent * 2 + 1;//假若右孩子比左孩子更小,则将child变量加一。if ((a[child + 1] < a[child]) && (child + 1) < size) {++child;}//当child>=size时表示已经到达叶子结点跳出循环while (child < size) {//双亲结点大于孩子结点交换if (a[parent] > a[child]) {Swap(&a[parent], &a[child]);//进行迭代parent = child;child = parent * 2 + 1;if ((a[child + 1] < a[child]) && (child + 1) < size) {++child;}}//若双亲结点不在大于孩子结点,说明已经迭代结束,不在需要继续进行迭代else {break;}}
}void HeapPop(Heap* php) {assert(php);assert(php->a > 0);php->a[0] = php->a[php->size - 1];php->size--;AdjustDown(php->a, php->size, 0);
}HPDataType HeapTop(Heap* php) {assert(php);assert(php->size > 0);return php->a[0];
}int HeapSize(Heap* php) {assert(php);return php->size;
}bool HeapEmpty(Heap* php) {assert(php);return php->size == 0;
}

        test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"int main() {int a[] = { 4,6,2,1,5,8,2,9 };Heap hp;HeapInit(&hp);for (int i = 0; i < sizeof(a) / sizeof(int); ++i) {HeapPush(&hp, a[i]);}for (int i = 0; i < sizeof(a) / sizeof(int); ++i) {printf("%d ", hp.a[i]);}printf("\n");while (!HeapEmpty(&hp)) {printf("%d ", HeapTop(&hp));HeapPop(&hp);}return 0;
}

        测试结果:

3.堆排序

        在上文中,已经较为介绍了堆的实现,现在实现堆的应用之一:堆排序。下午以降序排序为例进行介绍。

        对于堆的作用,我们在上文可以很清楚的得知,在小堆中,堆顶元素就是整个数据中最小的数据,但是对于后面的数据,并不一定呈现有序的方式排列,所以只能保证小堆的堆顶元素为最小值。所以若要将一组数据进行排序,其主要思路是先将其建堆,然后将堆顶元素放在数据最后,在将前n - 1个数据进行建堆,然后放到第n - 1个位置,以此循环递归,直至将数据排列完成。

        对于堆排序的排序原则为,降序排序为建大堆,升序排序为建小堆。以降序排序为例:当我们建立出小顶堆时,可以得出整列数据中最小的值,然后将最小值一致顺序表的最后位置,接着对前面的数据建堆,然后置换位置,以此迭代。若使用建大堆进行降序排列呢?每一次可以得到一个最大的值,然后将其放在第一个位置,对2 ~ n个位置的元素进行建堆。理论上可以,但是真正实行起来,对于第一个位置后的元素进行建堆这个步骤就会很麻烦,所以我们使用小堆进行降序排序。

        升序排序思路和上面基本一致,只有建小堆和大堆的区别。具体代码如下:

//降序排序
void HeapSort(HPDataType* a, int length) {// 建小堆for (int i = 1; i < length; i++) {AdjustUp(a, i);}//排序int end = length - 1;while (end > 0) {Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}int main() {int a[] = { 4,6,2,1,5,8,2,9 };HeapSort(a, sizeof(a) / sizeof(a[0]));for (int i = 0; i < sizeof(a) / sizeof(HPDataType); i++) {printf("%d ", a[i]);}printf("\n");return 0;
}

        测试结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/493129.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【代码随想录python笔记整理】第十三课 · 链表的基础操作 1

前言:本笔记仅仅只是对内容的整理和自行消化,并不是完整内容,如有侵权,联系立删。 一、链表 在之前的学习中,我们接触到了字符串和数组(列表)这两种结构,它们具有着以下的共同点:1、元素按照一定的顺序来排列。2、可以通过索引来访问数组中的元素和字符串中的字符。由此,…

力扣模板题:回文链表

请牢记检测回文串的模板 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ bool isPalindrome(struct ListNode* head) {int size0;struct ListNode* pointhead;while(point){size;pointpoint->next;}int arr…

微信小程序本地开发

微信小程序本地开发时不需要在小程序后台配置服务器域名直接在小程序项目中填写后端在本机的IP地址和端口号 如图&#xff08;第一步&#xff09; 填写地址后发现报错&#xff0c;url不是合法域名&#xff0c;则在详情设置不校验合法域名 如图&#xff08;第二歩&#xff09;…

学习vue3第二节(使用vite 创建vue3项目)

使用vite 创建vue3项目 node 安装请移步 node官网&#xff1a; https://nodejs.p2hp.com/ node 版本控制 请移步 nvm官网&#xff1a;https://nvm.uihtm.com/ vite 生成vue项目完整版 请移步 vite官网&#xff1a;https://cn.vitejs.dev/ 1、使用 npm 或者 yarn 创建vue3 项目…

9.网络游戏逆向分析与漏洞攻防-游戏网络架构逆向分析-接管游戏连接服务器的操作

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;游戏底层功能对接类GameProc的实现 码云地址&#xff08;master 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/titan 码云版本号&#xff1a;44c54d30370d3621c1e9ec3d7fa1e2a0…

数学建模【相关性模型】

一、相关性模型简介 相关性模型并不是指一个具体的模型&#xff0c;而是一类模型&#xff0c;这一类模型用来判断变量之间是否具有相关性。一般来说&#xff0c;分析两个变量之间是否具有相关性&#xff0c;我们根据数据服从的分布和数据所具有的特点选择使用pearson&#xff…

https://htmlunit.sourceforge.io/

https://htmlunit.sourceforge.io/ 爬虫 HtmlUnit – Welcome to HtmlUnit HtmlUnit 3.11.0 API https://mvnrepository.com/artifact/net.sourceforge.htmlunit/htmlunit/2.70.0 https://s01.oss.sonatype.org/service/local/repositories/releases/content/org/htmlunit…

启动spark-shell时报错java.lang.NumberFormatException: For input string: “0x100“

一、问题描述 安装完Spark后&#xff0c;启动spark shell时报错 java.lang.NumberFormatException: For input string: "0x100" 如下图&#xff1a; 二、解决办法 1.更换scala的版本 2.更改环境变量 使用vim编辑器打开用户的环境变量配置文件 vim ~/.bashrc s…

单片机51 定时器

一、基本概念 1.1简介 单片机的定时器是一种内部功能模块&#xff0c;用于产生计时、计数、延时等功能。定时器通常由一个或多个计数器和相关的控制逻辑组成。单片机的定时器可以运行在不同的工作模式下&#xff0c;以适应不同的计时和计数需求。 C51中的定时器和计数器是同…

代码随想录算法训练营day26

题目&#xff1a;39_组合总数&#xff08;没看题解&#xff09; 给定一个无重复元素的数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。 说明&#xff1a; 所有数字&…

设计模式(十) - 工厂方式模式

前言 在此前的设计模式&#xff08;四&#xff09;简单工厂模式中我们介绍了简单工厂模式&#xff0c;在这篇文章中我们来介绍下工厂方法模式&#xff0c;它同样是创建型设计模式&#xff0c;而且又有些类似&#xff0c;文章的末尾会介绍他们之间的不同。 1.工厂方法模式简介 …

AI赋能Oracle DBA:以自然语言与Oracle数据库互动

DBA AI助手&#xff1a;以自然语言与Oracle数据库互动 0. 引言1. AI赋能Oracle DBA的优势2. AI如何与Oracle数据库交互3. 自然语言查询的一些示例4. 未来展望 0. 引言 传统的Oracle数据库管理 (DBA) 依赖于人工操作&#xff0c;包括编写复杂的SQL语句、分析性能指标和解决各种…