当前位置: 首页 > news >正文

C语言经典算法之堆排序算法

目录

前言

建议

简介

A.建堆:

B.排序

一、代码实现

二、时空复杂度

A.时间复杂度

B.空间复杂度

三、稳定性

四、现实中的应用


前言

建议

1.学习算法最重要的是理解算法的每一步,而不是记住算法。

2.建议读者学习算法的时候,自己手动一步一步地运行算法。

tips:文中的对数均以2为底数

简介

堆排序是一种基于堆数据结构的排序算法。它分为两个主要步骤:建堆和排序。

A.建堆:


建堆的过程是将一个无序的数组构建成一个堆,通常采用最大堆(Max Heap)的形式。最大堆是一种特殊的二叉树,其中每个节点的值都大于或等于其子节点的值。下面是建堆的简单步骤

1.从最后一个非叶子节点开始: 最后一个非叶子节点的索引可以通过 (n/2 - 1) 计算,其中 n 是数组的长度。非叶子节点是指有子节点的节点。

2.进行堆调整(Heapify): 对于每个非叶子节点,进行堆调整,将当前节点与其子节点比较,确保当前节点是最大的。如果子节点的值较大,则交换节点值,然后递归地调整子树。

3.逐层向上重复: 重复步骤2,逐层向上处理非叶子节点,直到根节点。这样就确保了整个数组构成了一个最大堆。

B.排序

排序的过程是将一个无序的数据集合按照一定规则重新排列成有序的序列。以下是一般排序算法的简单过程

1.比较元素: 排序算法的第一步是通过比较元素来确定它们的相对顺序。比较可以根据不同的规则进行,例如升序或降序。

2.交换元素: 根据比较的结果,可能需要交换元素的位置以达到正确的顺序。这是排序算法中一个关键的步骤。

3.重复步骤12: 排序算法会持续比较和交换元素,直到整个数据集合中的元素都按照规则有序排列。

一、代码实现

 
#include <stdio.h>// 交换数组中两个元素的值
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}// 调整堆,保持最大堆性质
void maxHeapify(int arr[], int n, int i) {int largest = i;      // 初始化最大值的索引为根节点int left = 2 * i + 1;  // 左子节点的索引int right = 2 * i + 2; // 右子节点的索引// 如果左子节点比根节点大,则更新最大值的索引if (left < n && arr[left] > arr[largest]) {largest = left;}// 如果右子节点比当前最大值大,则更新最大值的索引if (right < n && arr[right] > arr[largest]) {largest = right;}// 如果最大值的索引不是根节点,交换它们的值并递归调整子树if (largest != i) {swap(&arr[i], &arr[largest]);maxHeapify(arr, n, largest);}
}// 建立最大堆
void buildMaxHeap(int arr[], int n) {// 从最后一个非叶子节点开始,逐个调整子树使其满足最大堆性质for (int i = n / 2 - 1; i >= 0; i--) {maxHeapify(arr, n, i);}
}// 堆排序主函数
void heapSort(int arr[], int n) {// 建立最大堆buildMaxHeap(arr, n);// 从最后一个元素开始,逐个将当前最大元素交换到数组末尾for (int i = n - 1; i > 0; i--) {swap(&arr[0], &arr[i]); // 将当前最大元素移动到数组末尾maxHeapify(arr, i, 0);   // 调整堆,保持最大堆性质}
}// 打印数组
void printArray(int arr[], int size) {for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() {int arr[] = {12, 11, 13, 5, 6, 7};int n = sizeof(arr) / sizeof(arr[0]);printf("原始数组: ");printArray(arr, n);heapSort(arr, n);printf("堆排序后的数组: ");printArray(arr, n);return 0;
}

 

这段代码首先建立一个最大堆,然后通过不断交换根节点和最后一个节点,并调整堆的方式,实现堆排序。

二、时空复杂度

A.时间复杂度


堆排序算法的时间复杂度主要由两个部分构成:建堆的时间复杂度排序的时间复杂度

建堆时间复杂度: 建堆的过程需要从最后一个非叶子节点开始,对每个节点进行堆调整(Heapify),使得整个数组构成一个最大堆。建堆的时间复杂度是O(n),其中n是数组的长度。

排序时间复杂度: 在建堆完成后,堆的根节点是数组中的最大元素。将根节点与数组末尾的元素交换,然后重新调整堆,这样就把最大的元素放到了数组末尾。重复这个过程,每次交换后堆的大小减一,直到整个数组有序。由于进行n次交换和调整,排序的时间复杂度也是O(nlogn)

因此,堆排序的总时间复杂度是建堆和排序时间复杂度的和O(n) + O(n * log n) = O(n * log n)

B.空间复杂度

堆排序的空间复杂度主要包括两个方面原地排序所需的空间递归调用的栈空间

原地排序: 堆排序是一种原地排序算法,它不需要额外的辅助数组来存储数据。排序过程中主要是通过在输入数组上进行元素交换,而不需要额外的存储空间。因此,原地排序的空间复杂度为 O(1)。

递归调用的栈空间: 在堆排序的建堆过程中,通常会使用递归调用或迭代方式来实现堆调整。递归调用会在调用栈中占用一定的空间,其空间复杂度与递归深度相关。最坏情况下,堆排序的递归深度为堆的高度,即logn,其中 n 是输入数组的长度。因此,递归调用的空间复杂度为 O(log n)

因此,原地排序和递归调用的空间开销,堆排序的总体空间复杂度为O(1) + O(log n) = O(log n)。这表明堆排序的空间需求相对较低,是一个空间效率较好的排序算法。

三、稳定性

在排序过程中,可能会交换相同值的元素的相对位置,导致相等元素之间的原始相对顺序被打乱。具体来说,在建堆和排序的过程中,会涉及到元素的交换操作。如果两个相等元素的顺序在排序之前是相对的,那么在排序之后,它们的相对顺序可能会发生变化。由于堆排序是通过不断交换堆顶元素和数组末尾元素,并调整堆的过程来实现的,这种交换可能破坏相同元素的相对顺序。因此,堆排序不是稳定的排序算法

四、现实中的应用

堆排序虽然在某些方面不如一些简单的排序算法直观,但在现实中仍然有一些应用场景,其中它的优势得到充分发挥:

优先队列: 堆排序常用于实现优先队列,其中最大堆或最小堆的性质使得可以高效地找到最大或最小元素。这在任务调度、事件模拟等领域有广泛应用。

堆作为数据结构的一部分: 堆结构本身作为一种数据结构,可以被应用于图算法中的最短路径算法(如Dijkstra算法)和最小生成树算法(如Prim和Kruskal算法)等。堆排序作为堆结构的一种附带应用,用于维护和操作堆。

外部排序: 在需要对大规模数据进行排序的情况下,堆排序相对于其他算法的稳定的时间复杂度使得它成为外部排序算法的一种选择。外部排序涉及到将大量数据从外部存储(例如硬盘)加载到内存中进行排序,然后再写回外部存储。

不稳定排序的需求: 在某些情况下,不需要保持相同值元素的相对顺序,而更关注排序算法的性能,此时堆排序可以是一个合适的选择。

相关文章:

C语言经典算法之堆排序算法

目录 前言 建议 简介 A.建堆&#xff1a; B.排序 一、代码实现 二、时空复杂度 A.时间复杂度 B.空间复杂度 三、稳定性 四、现实中的应用 前言 建议 1.学习算法最重要的是理解算法的每一步&#xff0c;而不是记住算法。 2.建议读者学习算法的时候&#xff0c;自己…...

前端笔试题(一)

1.vue如何实现数据的双向绑定 利用v-model来实现双向数据绑定 通过Object.defineProperty()来劫持各个属性的setter&#xff0c;getter&#xff0c;在数据变动时发布消息给订阅者&#xff0c;触发相应的监听回调来渲染视图 2.使用vue渲染大量数据时&#xff0c;如何进行优化…...

Springboot开发的大学生寝室考勤系统刷脸进出宿舍系统技术文档

宿舍出入系统 1.2采集学生人脸信息和宿管人脸信息 前端使用navigator.mediaDevices.getUserMedia&#xff08;考虑个浏览器的内核差异&#xff0c;此处以最新的标准API:navigator.mediaDevices.getUserMedia为例&#xff09;获取用户浏览器的摄像头并开启视频&#xff0c;使用…...

网络共享服务

存储类型&#xff1a;直连式&#xff08;DAS&#xff09;:距离最近&#xff0c;存储设备且直接连接到服务器上 存储区域网络&#xff08;SAN&#xff09;&#xff1a;适用于大型应用或数据库系统&#xff0c;可以使用文件的空间&#xff0c; 以及管理空间…...

资本主义的市场竞争?IBM总监Jerry Chow 谈量子计算的未来

​ 人物介绍&#xff1a;Jerry M.Chow博士在耶鲁大学取得物理博士学位。担任IBM量子系统总监&#xff0c;其研究重点是面向容错量子计算的多量子比特系统。他主要为IBM的量子系统路线图制定战略&#xff0c;与硬件团队领导者一起设定目标研究领域&#xff0c;同时也确保最佳的客…...

什么是残差矢量量化?

一、说明 基于残差矢量量化的神经音频压缩方法正在重塑现代音频编解码器的格局。在本指南中&#xff0c;了解 RVQ 背后的基本思想以及它如何增强神经压缩。 数据压缩在当今的数字世界中发挥着关键作用&#xff0c;促进信息的高效存储和传输。由于当今超过 80% 的互联网流量来自…...

计算机网络(第六版)复习提纲2

二、物理层 2.1 物理层基本概念 物理层协议常常成为物理层规程 物理层的主要任务为确定与传输媒体的接口有关的一些特性&#xff1a; 1.机械特性&#xff1a;指明接口所用接线器的尺寸等&#xff1b; 2.电气特性&#xff1a;指明接口电缆各条线上的电压范围&#xff1b; 3.功能…...

11k+star 开源笔记应用真香 centos部署教程

leanote binary installation on Mac and Linux (En) life edited this page on Jul 21, 2017 10 revisions Pages 26 Home How to develop leanote 如何开发leanote How to install leanote on Ubuntu? How to Upgrade Leanote Install Mongodb leanote api leanote …...

win下安装tensorflow

1首先ctrlaltdelete打开任务管理器查看GPU型号 2或者右键我的电脑然后如下方式查看显卡发现没有navida没有GPU...

SpringBoot 入门

1.SpringBoot介绍 1.1.什么是SpringBoot Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其中“Boot”的意思就是“引导”&#xff0c;Spring Boot 并不是对 Spring 功能上的增强&#xff0c;而是提供了一种快速开发 Spring应用的方式。 1.2.Spring Boot 特点 • 嵌…...

使用WebFlux处理WebSocket连接的全生命周期案例

使用WebFlux处理WebSocket连接的全生命周期案例 简介&#xff1a; 在Web应用程序开发中&#xff0c;WebSocket是一种用于实现双向通信的协议。Spring WebFlux提供了对WebSocket的支持&#xff0c;使您能够轻松地处理WebSocket连接和消息。本博客将介绍如何使用WebFlux处理WebS…...

【REST2SQL】10 REST2SQL操作指南

【REST2SQL】01RDB关系型数据库REST初设计 【REST2SQL】02 GO连接Oracle数据库 【REST2SQL】03 GO读取JSON文件 【REST2SQL】04 REST2SQL第一版Oracle版实现 【REST2SQL】05 GO 操作 达梦 数据库 【REST2SQL】06 GO 跨包接口重构代码 【REST2SQL】07 GO 操作 Mysql 数据库 【RE…...

199_二叉树的右视图

描述 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 思路 对树进行深度优先搜索&#xff0c;在搜索过程中&#xff0c;我们总是先访问右子树。那么对于每一层来说&#xff0c;…...

第七讲_css浮动

css浮动 1. 设置浮动2. 浮动的特点3. 浮动的影响4. 解决浮动的影响4.1 解决父元素高度塌陷的问题4.2 解决对兄弟元素影响问题 1. 设置浮动 浮动是通过float属性设置&#xff0c;float取值范围&#xff1a; none&#xff1a;不浮动&#xff0c;默认值。left&#xff1a;向左浮…...

2024秋招,顺丰科技测试开发工程师一面

前言 今天回顾一下&#xff0c;一个被捞的全流程面试经历 时间线 9月21日测评 10月26日技术一面&#xff0c;本来是11点半开始&#xff0c;我正做另一个笔试呢&#xff0c;突然给我打电话开面 20分钟结束&#xff0c;一开始以为KPI&#xff0c;结果给过了 10月31日技术二…...

基于apache的http文件服务配置

背景&#xff1a; 公司的产品使用的第三方模组可以OTA&#xff0c;厂家提供的是window开启软件&#xff0c;这样就可以在本机做http下载服务器&#xff0c;然后使用端口映射的方式&#xff0c;公开到外网&#xff0c;这样就可以进行4G网络访问内网服务器了。但这个有个弊端&am…...

连铸工艺和模铸工艺有什么区别。

问题描述&#xff1a;连铸工艺和模铸工艺有什么区别。 问题解答&#xff1a; 连铸工艺和模铸工艺在多个方面存在显著差异&#xff1a; 指代不同&#xff1a; 模铸是成批大量生产锻件的锻造方法。连铸即为连续铸钢的锻造方法。 工艺不同&#xff1a; 模铸在锻压机械的作用…...

pyqt treeWidget树生成

生成treeWidget树与获取treeWidget树节点的数据 # encodingUTF-8 import sys from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QApplication, QTreeWidgetItem, QLineEdit, QSpinBox, QComboBox from PyQt5.QtWidgets import QWidget from release_test import Ui_F…...

DataFunSummit:2023年云原生大数据峰会:核心内容与学习收获(附大会核心PPT下载)

随着数字化转型的深入推进&#xff0c;大数据技术已经成为企业获取竞争优势的关键因素之一。本次峰会汇聚了业界顶尖的大数据专家、企业领袖和技术精英&#xff0c;共同探讨云原生大数据领域的最新技术和趋势。本文将深入分析峰会的核心内容&#xff0c;并探讨参会者从中能学到…...

docker 容器添加指定网络地址

docker 容器添加指定网络地址 在搭建halo博客时&#xff0c;准备让 halo、mysql8.1、nginx 三个容器在同一个网段中&#xff0c;并指定IP。 实现docker内部容器之间网络互通。 查看容器网络信息命令 docker inspect 容器名各容器部署成功后网络效果如下&#xff1a; nginx …...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集&#xff0c;单周期执行&#xff1b;低功耗、CIP 独立外设&#xff1b;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel&#xff08;原始…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

ZYNQ学习记录FPGA(一)ZYNQ简介

一、知识准备 1.一些术语,缩写和概念&#xff1a; 1&#xff09;ZYNQ全称&#xff1a;ZYNQ7000 All Pgrammable SoC 2&#xff09;SoC:system on chips(片上系统)&#xff0c;对比集成电路的SoB&#xff08;system on board&#xff09; 3&#xff09;ARM&#xff1a;处理器…...