【数据结构】排序效率最优解之一:二叉树-堆

Hello everybody!今天打算给大家介绍一个功能比较强大的数据结构的基础,它不仅具有很高的应用价值而且排序效率很高。冒泡排序都知道叭,它的时间复杂度为O(n^2),而堆排序的时间复杂度为O(n*logn)。堆排序直接碾压冒泡排序。在c语言阶段,我曾给过大家qsort函数模拟实现的代码,我是以冒泡排序为底层逻辑实现的:时间复杂度为O(n^2)。而真正库文件中的qsort是以快排为底层逻辑实现的:时间复杂度为O(n*logn)。所以当我们排较长的数值时,肉眼可见的会发现自己模拟实现的qsort的效率远远不及库文件中的qsort。这就很好的体现了时间复杂度为O(n*logn)的数据结构的魅力所在!那好,废话不多说,我们直接开始叭!

1.二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。

1.顺序结构

顺序结构存储就是使用数组来存储,一般使用数组只适合完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

2.链式存储

二叉树的链式存储结构是指,用链表来表示一颗二叉树,即用链来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。链式结构又分为二叉链和三叉链。在当前的知识将借助一般都是二叉链,后面的高阶数据结构如红黑树等会用到三叉链。

2.堆的基本结构

注意堆有大堆和小堆之分。

小堆:每个结点上的数据都比子结点上的数据小,故根结点为最小值。

大堆:每个结点上的数据都比子结点上的数据小,故根结点为最大值。

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>typedef int HPDataType;
typedef struct Heap {HPDataType* a;int size;int capacity;
}HP;

这是堆的结构。其中a为数组名,堆上的数全部存放在这个数组中。虽然用数组存放堆,但它们的逻辑结构为堆。

因为:每个父结点的下标*2+1得到其左边的子结点,父结点的下标*2+2得其右边得子结点。

当然也可以反过来推:不管是左边的子结点还是右边的子节点,它们的下标-1与2取整即可得到其父结点。因为小数部分会被省略!

void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);//插入数据并向上调整调整
void HeapPop(HP* php);//删除堆顶(根节点)int HeapSize(HP* php);
HPDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);

这些是堆需要实现的一些接口。其中最核心的是HeapPush和HeapPop,因为涉及到数据的调整。

其他接口就比较简单,我也就简单介绍一下。

3.接口的实现

那我就按照小堆给大家实现叭!

3.1初始化&销毁

void HeapInit(HP* php) {assert(php);//检查php是否为空指针php->a = NULL;php->size = php->capacity = 0;
}void HeapDestroy(HP* php) {assert(php);free(php->a);//将a指向的动态空间还给操作系统php->a = NULL;//置空,避免出现野指针php->size = php->capacity = 0;
}

这两个接口比较简单,给出的注释比较详细。我就不过多赘述了!

3.2插入数据

void Swap(HPDataType* p1, HPDataType* p2) {int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void AdjustUp(HPDataType* a, HPDataType child) {int parent = (child - 1) / 2;//找父结点while (child > 0) {if (a[child] < a[parent]) {//当子结点小于父节点时,交换Swap(&a[child], &a[parent]);child = (child - 1) / 2;//找上一个子节点parent = (parent - 1) / 2;//找上一个父节点}else {break;}}
}void HeapPush(HP* php, HPDataType x) {assert(php);if (php->size == php->capacity) {int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;//当容量等于有效数据时,按二倍扩容HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapacity);//动态开辟空间assert(tmp);//检查是否开辟成功php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);//向上调整
}

我大概说一下这个接口的思路:1.首先看到HeapPush,先检查动态数组的容量是否够用,不够用的话就扩容。检查容量过后进入AdjustUp

2.在AdjustUp中,可以通过子节点找父节点,如果不满足小堆的规则,那么父节点的数值和子节点的数值交换,然后下标继续向上调整。直到子节点的下标为零(到达根结点时)结束。

通过调试会发现,确实是一个堆,这个接口的功能正常。

3.3删除堆顶

void AdjustDown(HPDataType* a, int size, int parent) {int child = parent * 2 + 1;//通过根结点找子结点while (child<size) {if (child + 1 < size && a[child + 1] < a[child]) {//如果左孩子大于右孩子,用右孩子child++;}if (a[parent] > a[child]) {Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;//继续向下调整}else {break;}}
}void HeapPop(HP* php) {assert(php);//检查指针assert(php->size > 0);//检查有效数据Swap(&php->a[0], &php->a[php->size - 1]);//头尾数值交换php->size--;//删除最后一个AdjustDown(php->a, php->size, 0);//根结点向下调整
}

3.4结点个数&访问根节点&判空

HPDataType HeapTop(HP* php) {assert(php);assert(php->size > 0);return php->a[0];
}int HeapSize(HP* php) {assert(php);return php->size;
}bool HeapEmpty(HP* php) {assert(php);return php->size == 0;
}

这三个接口就很简单啦!宝子们看懂应该没什么问题!

通过测试可以看到堆排序可以实现升序排列,并且效率更高!

4.代码

头文件:

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>typedef int HPDataType;
typedef struct Heap {HPDataType* a;int size;int capacity;
}HP;void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);//插入数据并向上调整调整
void HeapPop(HP* php);//删除堆顶(根节点)int HeapSize(HP* php);
HPDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);

源文件:

#include "Heap.h"
void HeapInit(HP* php) {assert(php);//检查php是否为空指针php->a = NULL;php->size = php->capacity = 0;
}void HeapDestroy(HP* php) {assert(php);free(php->a);//将a指向的动态空间还给操作系统php->a = NULL;//置空,避免出现野指针php->size = php->capacity = 0;
}
void Swap(HPDataType* p1, HPDataType* p2) {int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void AdjustUp(HPDataType* a, HPDataType child) {int parent = (child - 1) / 2;//找父结点while (child > 0) {if (a[child] < a[parent]) {//当子结点小于父节点时,交换Swap(&a[child], &a[parent]);child = (child - 1) / 2;parent = (parent - 1) / 2;}else {break;}}
}void HeapPush(HP* php, HPDataType x) {assert(php);if (php->size == php->capacity) {int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;//当容量等于有效数据时,按二倍扩容HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapacity);//动态开辟空间assert(tmp);//检查是否开辟成功php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);//向上调整
}void AdjustDown(HPDataType* a, int size, int parent) {int child = parent * 2 + 1;while (child<size) {if (child + 1 < size && a[child + 1] < a[child]) {child++;}if (a[parent] > a[child]) {Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else {break;}}
}void HeapPop(HP* php) {assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);php->size--;AdjustDown(php->a, php->size, 0);
}HPDataType HeapTop(HP* php) {assert(php);assert(php->size > 0);return php->a[0];
}int HeapSize(HP* php) {assert(php);return php->size;
}bool HeapEmpty(HP* php) {assert(php);return php->size == 0;
}

测试文件:

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

5.结语

那今天的讨论就到这里喽!不知道大家是否有所收获呢?可以把自己的疑问发在评论区!

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

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

相关文章

ELK高级搜索,深度详解ElasticStack技术栈-上篇

前言 1、黑马视频地址&#xff1a;java中级教程-ELK高级搜索&#xff0c;深度详解ElasticStack技术栈 2、本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删除 1. 课程简介 1.1 课程内容 ELK是包含但不限于Elasticsearch&#xff08;简称es&#xff09;、Lo…

小狐狸ChatGPT付费创作系统V2.3.4独立版 +WEB端+ H5端最新去弹窗授权

ChatGPT付费创作系统V2.3.4版本优化了很多细节&#xff0c;如果使用着2.2.9版本建议没升级的必要。该版本为编译版无开源&#xff0c;2.3.X版本开始官方植入了更多的后门和更隐性的弹窗代码&#xff0c;后门及弹窗处理起来更麻烦。特别针对后台弹窗网址、暗链后门网址全部进行了…

git rebase冲突说明(base\remote\local概念说明)

主线日志及修改 $ git log master -p commit 31213fad6150b9899c7e6b27b245aaa69d2fdcff (master) Author: Date: Tue Nov 28 10:19:53 2023 08004diff --git a/123.txt b/123.txt index 294d779..a712711 100644 --- a/123.txtb/123.txt-1,3 1,4 123 4^Mcommit a77b518156…

网络和Linux网络_6(应用层)HTTPS协议(加密解密+中间人攻击+证书)

目录 1. HTTPS协议介绍 1.1 加密解密和秘钥的概念 1. 2 为什么要加密 2. 对称加密和非对称加密 2.1 只使用对称加密 2.2 只使用非对称加密 2.3 双方都使用非对称加密 2.4 使用非对称加密对称加密 2.5 中间人攻击MITM 3. 证书的概念和HTTPS的通信方式 3.1 CA认证机构…

Redis 事件轮询

1 Redis 为什么快 数据存在内存中, 直接操作内存中的数据单线程处理业务请求避免了多线的上下文切换, 锁竞争等弊端使用 IO 多路复用支撑更高的网络请求使用事件驱动模型, 通过事件通知模式, 减少不必要的等待… 这些都是 Redis 快的原因。 但是这些到了代码层面是如何实现的呢…

基于SSM的论文管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

集合框架(二)LinkedList的常见使用

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍LinkedList的常见使用以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xff0c;友友们有任何问题可以在评论区留言 目…

Vue2问题:如何全局使用less和sass变量?

前端功能问题系列文章&#xff0c;点击上方合集↑ 序言 大家好&#xff0c;我是大澈&#xff01; 本文约2400字&#xff0c;整篇阅读大约需要4分钟。 本文主要内容分三部分&#xff0c;如果您只需要解决问题&#xff0c;请阅读第一、二部分即可。如果您有更多时间&#xff…

大一学编程怎么学?刚接触编程怎么学习,有没有中文编程开发语言工具?

大一学编程怎么学&#xff1f;刚接触编程怎么学习&#xff0c;有没有中文编程开发语言工具&#xff1f; 1、大一刚开始学编程&#xff0c;面对复杂的代码学习非常吃力&#xff0c;很难入门。建议刚接触编程可以先学习中文编程&#xff0c;了解其中的编程逻辑&#xff0c;学编程…

LeetCode [中等]3. 无重复字符的最长子串

3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 1. 滑动窗口&#xff08;Sliding Window&#xff09;&#xff1a; 滑动窗口是一种用于处理数组或列表的子数组或子序列的问题…

Vue基础入门(四):Vue3快速开发模板

快速开发Vue的解决方案 ​ Vue 的开发需要的 node 环境&#xff0c;其实上在开发的过程中会遇到一些你想不到的问题&#xff0c;比如 npm工具的版本和 node 环境不匹配&#xff08;你把其他项目导入到自己的环境&#xff09; ​ vue-element-admin&#xff08;是一个官方提供…

leetCode 216.组合总和 III + 回溯算法 + 剪枝 + 图解 + 笔记

找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回 示例 1: 输入: k 3, n 7 输出: [[1,2,4]] 解释…

pinia从入门到使用

pinia: 比vuex更适合vue3的状态管理工具&#xff0c;只保留了vuex 原有的 state, getters&#xff0c;actions 作用等同于 data computed methods&#xff0c;可以有多个 state 1.安装创建导入 安装&#xff1a;npm install pinia 或 yarn add pinia 创建stores/index.js inde…

Jmeter接口测试和性能测试

目前最新版本发展到5.0版本&#xff0c;需要Java7以上版本环境&#xff0c;下载解压目录后&#xff0c;进入\apache-jmeter-5.0\bin\&#xff0c;双击ApacheJMeter.jar文件启动JMemter。 1、创建测试任务 添加线程组&#xff0c;右击测试计划&#xff0c;在快捷菜单单击添加-…

VSCode Vue 开发环境配置

Vue是前端开发中的重要工具与框架&#xff0c;可以保住开发者高效构建用户界面。 Vue2官方文档&#xff1a;https://v2.cn.vuejs.org/ Vue3官方文档&#xff1a;https://cn.vuejs.org/ Vue的安装和引用 Vue2的官方安装指南&#xff1a;https://v2.cn.vuejs.org/v2/guide/ins…

ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO

ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO 概述 上节 ESP32-Web-Server 实战编程-通过网页控制设备的 GPIO 讲述了如何通过网页控制一个 GPIO。本节实现在网页上控制多个 GPIO。 示例解析 前端设计 前端代码建立了四个 GPIO&#xff0c;如下死 GPIO 2 在前端的…

配置 Mantis 在 Windows 上的步骤

配置 Mantis Bug Tracker 在 Windows 上的步骤 Mantis Bug Tracker 是一款开源的缺陷跟踪系统&#xff0c;用于管理软件开发中的问题和缺陷。在 Windows 环境下配置 Mantis 可以帮助开发者更方便地进行项目管理。以下是一个详细的教程&#xff0c;包含了 EasyPHP Devserver 和…

路径规划之A*算法

系列文章目录 路径规划之Dijkstra算法 路径规划之Best-First Search算法 路径规划之A*算法 路径规划之A*算法 系列文章目录前言一、前期准备1.1 算法对比1.2 数学式方法1.3 启发式方法 二、A*算法2.1 起源2.2 思想2.3 启发式函数2.4 过程2.5 案例查看 前言 之前提过Dijkstra算…

leetcode 1670

leetcode 1670 解题思路 使用2个deque作为类的成员变量 code class FrontMiddleBackQueue { public:deque<int> left;deque<int> right;FrontMiddleBackQueue() {}void pushFront(int val) {left.push_front(val);if(left.size() right.size()2){right.push_fr…

如何使用ArcGIS Pro制作一张北极俯视地图

地图的表现形式有很多种&#xff0c;经常我们看到的地图是以大西洋为中心的地图&#xff0c;还有以太平洋为中心的地图&#xff0c;今天要给大家介绍的地图是从北极上方俯视看的地图&#xff0c;这里给大家讲解一下制作方法&#xff0c;希望能对你有所帮助。 修改坐标系 制作…