数据结构——堆排序

什么是堆排序

堆排序就是利用堆(假设利用大堆)进行排序的算法。他的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将他移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值。如此反复执行,便能得到一个有序序列了。

建堆 

首先我们需要对数组中元素int a[] = { 6,1,2,7,9,3,4,5,10,8 }建大堆。下面我们开始建堆。

向下调整 

 我们知道完全二叉树的孩子结点child=parent*2+1;我们建大堆就需要找大的结点的值。

我们假设左结点的值大,如果右节点a[child+1]>左节点a[child],那child=child+1;即

	while (){if (a[child + 1] > a[child]){child = child + 1;}}

接下来我们用child的值和父亲节点parent的值比较,如果,a[child]>a[parent],则交换这两个值,然后新的parent结点为child,新的child=新的parent*2+1,如果不满足这个条件的话,就跳出break。

void AdjustDown(int* a, int n, int parent)
{int child = parent * 2 + 1;while (){if (a[child + 1] > a[child]){child = child + 1;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

这个时候我们要判断这个循环什么时候停止,child每次和parent交换完都会child=parent*2+1;当child>n结点个数的时候就不需要继续再往下找大了,即循环停止。

while (child<n)

这个时候我们还需要考虑的是我们在找左节点和右节点中较大的值的时候,如果此时child==n的话,那child+1就会越界了。所以我们要加上限定条件child+1<n,即完整代码

void AdjustDown(int* a, int n, int parent)
{int child = parent * 2 + 1;while (child<n){if (child + 1 < n && a[child + 1] > a[child]){child = child + 1;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

建大堆

这个时候我们已经完成了向下调整。此时我们只需要把数组中数据建大堆即可。

首先我们要先用for循环从最后一个结点开始找他的父节点和左右节点中大的与父节点比较,所以找的第一个父节点为(n-1)-1/2;i--,直到i>=0时一直向下调整建堆。

void HeapSort(int*a,int n)
{for (int i = (n - 1 - 1) / 2; i >= 0; i--)//建堆{AdjustDown(a, n, i);}}

这个时候大堆就已经建好了,此时堆顶元素是这个堆的最大值,我们用堆尾元素(end=n-1)与堆顶元素,此时最大值就来到了堆尾,然后继续向下调整保证前n-1个数还能保持大堆,再把end--,把最大的固定其位置,直到n=0时停止

int end = n - 1;
while (end > 0)
{Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;
}

测试

这个时候堆排序就完整的写完啦。我们测试运行。

int main()
{int a[] = { 6,1,2,7,9,3,4,5,10,8 };int n = sizeof(a) / sizeof(a[0]);for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");HeapSort(a, n);for (int i = 0; i < n; i++){printf("%d ", a[i]);}return 0;
}

复杂度

堆排序的效率到底有多高呢?我们来分析一下。它的运行时间主要消耗在初始构建堆和重建堆的反复筛选上。在构建堆的过程中,因为我们是完全二叉树从最下层最右边的非终端节点开始构建,将他与其孩子进行比较,若有必要进行交换,对于每个非终端结点来说,其实最多进行两次比较和呼唤操作,因次整个构建堆的时间复杂度为O(n)。

在正式排序时,第 i 次取堆顶记录重建堆需要用 O ( logi )的时间(完全二叉树的某个结点到根结点的距离为 (log i+1),并且需要取 n -1次堆顶记录,因此,重建堆的时间复杂度为 O ( nlogn )。
所以总体来说,堆排序的时间复杂度为 O ( nlogn )。由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最坏和平均时间复杂度均为 O ( nlogn )。这在性能上显然要远远好过于冒泡、简单选择、直接插入的 O (n2)的时间复杂度了。
空间复杂度上,它只有一个用来交换的暂存单元,也非常的不错。不过由于记录的比较与交换是跳跃式进行的,因此堆排序也是一种不稳定的排序方法。
另外,由于初始构建堆所需的比较次数较多,因此,它并不适合待排序序列个数较少的情况。

源码

#include<stdio.h>void Swap(int* p1, int* p2)//交换
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void AdjustDown(int* a, int n, int parent)//向下调整
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] > a[child])//判断左右孩子较大的{child = child + 1;}if (a[child] > a[parent])//判断孩子结点与父节点大小{Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
void HeapSort(int*a,int n)
{for (int i = (n - 1 - 1) / 2; i >= 0; i--)//建堆{AdjustDown(a, n, i);}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}
int main()
{int a[] = { 6,1,2,7,9,3,4,5,10,8 };int n = sizeof(a) / sizeof(a[0]);for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");HeapSort(a, n);for (int i = 0; i < n; i++){printf("%d ", a[i]);}return 0;
}

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

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

相关文章

transforms图像增强(二)

一、图像变换 1、transforms.Pad transforms.Pad是一个用于对图像边缘进行填充的数据转换操作。 参数&#xff1a; padding&#xff1a;设置填充大小。可以是单个整数&#xff0c;表示在上下左右四个方向上均填充相同数量的像素&#xff1b;也可以是一个包含两个整数的元组…

PEFT: 在低资源硬件上对十亿规模模型进行参数高效微调

1 引言 最近&#xff0c;深度学习的研究中出现了许多大型预训练模型&#xff0c;例如 GPT-3、BERT 等&#xff0c;这些模型可以在多种自然语言处理任务中取得优异的性能表现。而其中&#xff0c;ChatGPT 模型因为在对话生成方面的表现而备受瞩目&#xff0c;成为了自然语言处理…

webgl调试之排查内存泄漏

内存泄漏自然而然是要看内存是不是涨了 然后我们如何确认泄露了呢&#xff0c;我们需要把代码梳理清楚&#xff0c;知道哪个时机&#xff0c;在delete&#xff0c;在create&#xff0c;那么这个时候&#xff0c;按道理&#xff0c;delete了n个对象&#xff0c;create了N个对象&…

Python从入门到网络爬虫(MySQL链接)

前言 在实际数据分析和建模过程中&#xff0c;我们通常需要从数据库中读取数据&#xff0c;并将其转化为 Pandas dataframe 对象进行进一步处理。而 MySQL 数据库是最常用的关系型数据库之一&#xff0c;因此在 Python 中如何连接 MySQL 数据库并查询数据成为了一个重要的问题…

实验笔记之——基于Linux服务器复现Instant-NGP及常用的tmux指令

之前博客实现了基于windows来复现Instant-NGP&#xff0c;本博文在linux服务器上测试 实验笔记之——基于windows复现Instant-NGP-CSDN博客文章浏览阅读444次&#xff0c;点赞15次&#xff0c;收藏7次。之前博客对NeRF-SLAM进行了调研&#xff0c;本博文先复现一下Intant-NGP。…

QT----Visual stdio翻金币案例,附源码

历经一个月&#xff0c;各种事情磕磕绊绊&#xff0c;终于结束了&#xff0c;自己还是太菜了 案例的文档写的教程已经很详细&#xff0c;这边主要是记录一些问题 github代码 gitee代码 1、图片无法加载 一开始加载首页图片和标题出不来&#xff0c;结果是paintEvent重写的字打…

rust 注释文档生成 cargo doc

rust的cargo文档生成 只需要在每个函数写清楚注释&#xff0c;就可以自动生成文档&#xff0c;很方便 即不用写文档&#xff0c;又可以快速查看&#xff0c;是开发rust的必备技能 rust安装和开发环境配置&#xff0c;可以参考&#xff1a;链接 1.写注释的方法 连续三个 \ 即…

【已解决】js定义对象属性是.如何访问

当变量没有length属性的时候&#xff0c;可能是个对象变量&#xff0c;当有键值对的时候就可能是个对象&#xff0c;读者都知道的是&#xff0c;用typeof(变量)可以查看属性&#xff0c;今天本文解决的问题是如果js定义对象中属性是"点"如何访问 问题再现 var a {…

【性能测试入门】详解客户端性能测试和服务器端性能测试!

一&#xff1a;客户端性能测试和服务器端性能测试 客户端性能测试和服务器端性能测试是两个不同但相关的概念: 客户端性能测试: - 测试应用程序客户端(如Web浏览器、移动应用等)的性能,例如加载时间,响应时间等。 - 测试在不同系统配置(CPU、内存、网络等)下客户端的运行性…

【HarmonyOS】装饰器下的状态管理与页面路由跳转实现

从今天开始&#xff0c;博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”&#xff0c;对于刚接触这项技术的小伙伴在学习鸿蒙开发之前&#xff0c;有必要先了解一下鸿蒙&#xff0c;从你的角度来讲&#xff0c;你认为什么是鸿蒙呢&#xff1f;它出现的意义又是…

Python中的cls语法

在Python中&#xff0c;cls 是一个用于指代类本身的约定性名称&#xff0c;通常用作类方法&#xff08;class method&#xff09;中的第一个参数。cls 类似于 self&#xff0c;它是对类的引用&#xff0c;而不是对实例的引用。cls 通常在类方法中用于访问类级别的属性和方法。举…

计算机中的数据运算

放上计算机中的数据的表示方法 计算机中的数据表示方法-CSDN博客 补码的运算&#xff1a; 连同符号位一起相加&#xff0c;符号位产生的进位自然丢掉&#xff0c;这里要特别注意机器数的位数&#xff0c;计算数的位数决定了可以存放的数据的大小&#xff0c;加减产生的数据的…