排序(堆排序、快速排序、归并排序)-->深度剖析(二)

前言

前面介绍了冒泡排序、选择排序、插入排序、希尔排序,作为排序中经常用到了算法,还有堆排序快速排序归并排序

堆排序(HeaSort)

堆排序的概念

堆排序是一种有效的排序算法,它利用了完全二叉树的特性。在C语言中,堆排序通常通过构建一个最大堆(或最小堆),然后逐步调整堆结构,最终实现排序。

代码实现

堆排序是一种基于二叉堆的排序算法,它通过将待排序的元素构建成一个二叉堆,然后逐步移除堆顶元素并将其放置在数组的尾部,同时维护堆的性质,直至所有元素都被排序。

注意:第一个for循环中的(n-1-1)/ 2 的含义

  • 第一个减1是由n-1个元素
  • 第二个减1是除以2是父亲节点。以为我们调整的是每一个根节点。(非叶子节点)
//堆排序
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[end], &a[0]);AdjustDown(a, end, 0);--end;}	
}

其中AdjustDown是建立堆的函数,我们要建立一个大堆,将替换到上上面的小值,向下调整,保持大堆的结构。

代码如下:

//向下调整
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++;}if (a[parent] < a[child]){Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else{break;}}}

堆排序的复杂度分析

堆排序是一种常用的排序算法,其时间复杂度通常为O(nlogn)。在C语言中实现堆排序时,时间复杂度的分析主要涉及到两个阶段:构建初始堆和进行堆排序。

  • 构建初始堆:从最后一个非叶子节点开始,逐步向上调整,直到根节点满足堆的性质。这个过程的时间复杂度为O(n),因为需要对每个非叶子节点进行一次调整。
  • 进行堆排序:堆排序的过程涉及到多次交换堆顶元素和最后一个元素,并对剩余的元素进行调整。每次交换后,堆的大小减一,并对新的堆顶元素进行调整。这个过程的时间复杂度为O(nlogn),因为每次调整的时间复杂度为O(logn),总共需要进行n-1次调整。

快速排序(Quick Sort)

快速排序的概念

快速排序(Quick Sort)是一种高效的排序算法,它的基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,然后再分别对这两部分记录继续进行排序,以达到整个序列有序的目的。在C语言中,快速排序的实现通常涉及到递归函数的编写,以及对数组进行分区(partition)操作。

霍尔版本(hoare)

在这里插入图片描述

在这里我们是要,定义一个关键字(keyi)进行分区,然后分别向左右进行递归。

代码实现

int part1(int* a, int left, int right)
{int mid = GetmidNum(a,left,right);Swap(&a[left], &a[mid]);int keyi = left;while (left < right){while (left < right && a[right] >= a[keyi])right--;while (left < right && a[left] <=a[keyi])left++;Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);keyi = left;return keyi;
}

挖坑法

挖坑法类似于霍尔方法,挖坑就是记住关键字,保证关键字就是一个坑位,比关键字值小(大)的时候就入坑位,此时,这个值位置作为新的坑位直至两个前后指针指向同一个坑位。

在这里插入图片描述

代码实现

int part2(int* a, int left, int right)
{int mid = GetmidNum(a, left, right);Swap(&a[left], &a[mid]);int keyi = a[left];int hole = left;while (left < right){while (left < right && a[right] >= keyi)right--;Swap(&a[hole], &a[right]);hole = right;while (left < right && a[left] <= keyi)left++;Swap(&a[hole], &a[left]);hole = left;}Swap(&keyi, &a[hole]);keyi = left;return keyi; }

前后指针法

  • prev 指针初始化为数组的开始位置,cur 指针初始化为 prev 的下一位置。

  • cur 指针向前遍历数组,寻找小于或等于基准值的元素,而 prev 指针则跟随 cur 指针的移动,直到 cur 找到一个小于基准值的元素。

  • 一旦找到这样的元素,prevcur 指针之间的元素都会被交换,然后 cur 指针继续向前移动,直到找到下一个小于基准值的元素,或者到达数组的末尾。最后,基准值会被放置在 prev 指针当前的位置,完成一次排序

在这里插入图片描述

代码实现

int part3(int* a, int left, int right)
{int mid = GetmidNum(a, left, right);Swap(&a[left], &a[mid]);int keyi = left;int cur = left + 1;int prev = left;while (cur <= right){while (a[cur] < a[keyi] && ++prev != cur)Swap(&a[cur], &a[prev]);++cur;}Swap(&a[prev], &a[keyi]);keyi = prev;return keyi;
}

递归实现

以上都是递归方法,通过调用分区进行排序。

void QuickSort(int* a, int left, int right)
{if (left >= right)return;int key = part3(a, left, right);QuickSort(a, left, key - 1);QuickSort(a, key + 1, right);}

快速排序迭代实现(利用栈)参考:栈和队列

基本步骤
  1. 初始化栈:创建一个空栈,用于存储待排序子数组的起始和结束索引。
  2. 压栈:将整个数组的起始和结束索引作为一对入栈。
  3. 循环处理,在栈非空时,重复以下步骤:
    • 弹出一对索引(即栈顶元素)来指定当前要处理的子数组。
    • 选择子数组的一个元素作为枢轴(pivot)进行分区。
    • 进行分区操作,这会将子数组划分为比枢轴小的左侧部分和比枢轴大的

代码实现

void QuickSortNonR(int* a, int left, int right)
{ST st;STInit(&st);STpush(&st, left);STpush(&st, right);while (!STEmpty(&st)){int end = STTop(&st);STPop(&st);int begin = STTop(&st);STPop(&st);int keyi = part3(a, begin, end);if (keyi + 1 < end){STpush(&st, keyi + 1);STpush(&st, end);}if (begin < keyi - 1){STpush(&st, begin);STpush(&st, keyi - 1);}}STDestroy(&st);
}

快速排序的优化

优化角度从两个方面切入

  1. 在选择关键字的(基准值)时候,如果我们碰到了,有序数组,那么就会,减低排序效率。
    • 方法一:三数取中,即区三个关键字先进行排序,将中间数作为关键字,一般取左端右端和中间值。
    • 方法二:随机值。利用随机数生成。

三数取中代码实现

int GetmidNum(int* a, int begin, int end)
{int mid = (begin + end) / 2;if (a[begin] < a[mid]){if (a[mid] < a[end]){return mid;}else if(a[end]<a[begin]){return begin;}else{return end;}}else //a[begin]>a[mid]{if (a[begin] < a[end]){return begin;}else if (a[end] < a[mid]){return mid;}else{return end;}}

随机选 key(下标) 代码实现

srand(time(0));
int randi = left + (rand() % (right - left));
Swap(&a[left], &a[randi]);

快速排序复杂度分析

  • 在平均情况下,快速排序的时间复杂度为O(n log n),这是因为每次划分都能够将数组分成大致相等的两部分,从而实现高效排序。在最坏情况下,快速排序的时间复杂度为O(n^2)
  • 除了递归调用的栈空间之外,不需要额外的存储空间,因此空间复杂度是O(log n)。在最坏情况下,快速排序的时间复杂度可能是 O(n),这是因为在最坏情况下,递归堆栈空间可能会增长到线性级别。

根据上述描述,优化快速排序是必要的。

归并排序(Merge Sort)

在这里插入图片描述

归并排序的概念

归并排序(Merge Sort)是一种基于分治策略的排序算法,它将待排序的序列分为两个或以上的子序列,对这些子序列分别进行排序,然后再将它们合并为一个有序的序列。归并排序的基本思想是将待排序的序列看作是一系列长度为1的有序序列,然后将相邻的有序序列段两两归并,形成长度为2的有序序列,以此类推,最终得到一个长度为n的有序序列。

基本操作:

  • 分解:将序列每次折半划分,递归实现,直到子序列的大小为1。
  • 合并:将划分后的序列段两两合并后排序。在每次合并过程中,都是对两个有序的序列段进行合并,然后排序。这两个有序序列段分别为 R[low, mid]R[mid+1, high]。先将他们合并到一个局部的暂存数组 R2 中,合并完成后再将 R2 复制回 R 中。

代码实现(递归)

void _MergeSort(int* a, int* tmp, int begin, int end)
{if (begin >= end)return;int mid = (begin + end) / 2;_MergeSort(a, tmp, begin, mid - 1);_MergeSort(a, tmp, mid + 1, end);int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] > a[begin2]){tmp[i++] = a[begin2++];}else{tmp[i++] = a[begin1++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}_MergeSort(a, tmp, 0, n-1);free(tmp);
}

代码实现(迭代)

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){for (int i = 0; i < n; i =2* gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;int j = i;if (end1 >= n || begin2 >= n){break;}if (end2 >= n){end2 = n-1;}while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;}free(tmp); 
}

归并排序复杂度分析

  • 时间复杂度是 O(n log n),其中 n 是待排序元素的数量。这个时间复杂度表明,归并排序的执行速度随着输入大小的增加而线性增加,但不会超过对数级的增长。
  • 空间复杂度为 O(n),在数据拷贝的时候malloc一个等大的数组。

总结

p[j++] = a[begin2++];
}
}

		while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;
}
free(tmp); 

}


## 归并排序复杂度分析* 时间复杂度是 O(n log n),其中 n 是待排序元素的数量。这个时间复杂度表明,归并排序的执行速度随着输入大小的增加而线性增加,但不会超过对数级的增长。
* 空间复杂度为 O(n),在数据拷贝的时候malloc一个等大的数组。# 总结![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/8d8d45e2fc8b4b0fa4747b27d20cd50c.png)

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

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

相关文章

复分析——第9章——椭圆函数导论(E.M. Stein R. Shakarchi)

第 9 章 椭圆函数导论 (An Introduction to Elliptic Functions) The form that Jacobi had given to the theory of elliptic functions was far from perfection; its flaws are obvious. At the base we find three fundamental functions sn, cn and dn. These functio…

商汤上海AI实验室联合发布:自动驾驶全栈式高精度标定工具箱(含车、IMU、相机、激光雷达等的标定)

前言 在自动驾驶技术飞速发展的今天&#xff0c;传感器的精确标定对于确保系统性能至关重要。SensorsCalibration&#xff0c;一个专为自动驾驶车辆设计的标定工具箱&#xff0c;提供了一套全面的解决方案&#xff0c;用于校准包括IMU、激光雷达、摄像头和雷达在内的多种传感器…

基于Java平价平价汽车租赁系统设计和实现(源码+LW+部署讲解)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

《RepViT Revisiting Mobile CNN From ViT Perspective》

期刊&#xff1a;CVPR 年份&#xff1a;2024 代码&#xff1a;http://https: //github.com/THU-MIG/RepViT 摘要 最近&#xff0c;与轻量级卷积神经网络(CNN)相比&#xff0c;轻量级视觉Transformer(ViTs)在资源受限的移动设备上表现出了更高的性能和更低的延迟。研究人员已…

无法访问指向的web服务器(或虚拟主机)的目录,请检查网络设置

微信公众平台,进行业务域名、JS接口安全域名、网页授权域名配置时&#xff0c;遇到的问题中有&#xff1a;无法访问指向的web服务器&#xff08;或虚拟主机&#xff09;的目录&#xff0c;请检查网络设置&#xff0c;这里简单记录一下处理过程。 关于这个问题首先保证下载…

【基于R语言群体遗传学】-1-哈代温伯格基因型比例

前言 群体遗传学是研究生物群体中基因的分布、基因频率和基因型频率的维持和变化的学科。它不仅探讨遗传病的发病频率和遗传方式&#xff0c;还研究基因频率和变化的规律&#xff0c;为预防、监测和治疗遗传病提供重要信息。R语言作为一种强大的统计分析工具&#xff0c;在群体…

mybatis实现多表查询

mybatis高级查询【掌握】 1、准备工作 【1】包结构 创建java项目&#xff0c;导入jar包和log4j日志配置文件以及连接数据库的配置文件&#xff1b; 【2】导入SQL脚本 运行资料中的sql脚本&#xff1a;mybatis.sql 【3】创建实体来包&#xff0c;导入资料中的pojo 【4】User…

TypeScript Project References npm 包构建小实践

npm 包输出 es/cjs 产物 在开发一个 npm 包时&#xff0c;通常需要同时输出 ES 模块和 CommonJS 模块的产物供不同的构建进行使用。在只使用tsc进行产物编译的情况下&#xff0c;我们通常可以通过配置两个独立的 tsconfig.json 配置文件&#xff0c;并在一个 npm script 中 执…

7.1作业

1.思维导图 2.在堆区申请两个长度为32的空间&#xff0c;实现两个字符串的比较【非库函数实现】 (1)定义函数&#xff0c;在对区申请空间 两个申请&#xff0c;主函数需要调用2次 (2)定义函数&#xff0c;实现字符串的输入 void input(char *p) (3)调用函数实现字符串比较…

BUT000增强字段BAPI结构激活出错(BUPA_CENTRAL_CI_CHANGE)

导语&#xff1a;BP主数据增强字段&#xff0c;需要使用BAPI&#xff1a;BUPA_CENTRAL_CI_CHANGE进行值写入&#xff0c;但是在SAP 2023以后的版本&#xff0c;激活会出错&#xff0c;原因是因为SAP的一个结构同时包含了BUS00_EEW以及BUS00_EEWX两个结构&#xff0c;导致结构字…

Spring Security 认证流程

Spring Scurity是spring生态下用于认证和授权的框架&#xff0c;具有高度的灵活性和可扩展行&#xff0c;本节主要对Spring Security的认证过程中进行概括性的介绍&#xff0c;主要介绍在该过程中&#xff0c;会涉及到哪些组件以及每个组件所承担的职责&#xff0c;希望大家可以…

电脑录音软件哪个好?7款录制音频工具大盘点,赶快学起来!(2024)

也许你渴望提取你最喜欢的节目的背景音乐&#xff0c;或者你希望录制自己的声音制作教程。如果是这样&#xff0c;你就需要一款优秀的电脑录音软件&#xff0c;来帮助你捕捉任何你想要的声音&#xff0c;而且不会损失音质。目前市场上存在着大量的录制音频工具&#xff0c;面对…

锁相环相位噪声仿真代码-汇总

24小时自动发货 所设计的压控振荡器输入电压为0.625V时&#xff0c;输出大致为500Mhz&#xff1b;输入电压为1.559时&#xff0c;输出电压大致为1Ghz 1.文件夹里面各个文件作用&#xff08;包括参考书PLL PHASE NOISE ANALYSIS、lee的射频微电子、以及前人留下的matlab文件还有…

Ubuntu(通用)—网络加固—防DNS污染和ARP欺骗

1. 防DNS污染 DNS协议&#xff0c;把域名解析成ip地址&#xff0c;udp&#xff0c;这个过程会暴露访问的域名&#xff0c; 对这一传输过程加密&#xff08;传输层用tcp&#xff09;即为DoH(DNS over HTTPS)。 Browser(firefox)加固 由于Cloudflare、Quad8的DoH服务器不能用&…

Dns被莫名篡改的问题定位(笔记)

引言&#xff1a;最近发现用户的多台机器上出现了Dns被莫名修改的问题&#xff0c;从系统事件上看并未能正常确定到是那个具体软件所为&#xff0c;现在的需求就是确定和定位哪个软件具体所为。 解决思路&#xff1a; 首先到IPv4设置页面对Dns进行设置&#xff1a;通过ProcExp…

缺失d3dx9_43.dll是怎么回事?教你几种靠谱的解决方法

在日常生活和工作中&#xff0c;电脑已经成为我们不可或缺的工具。然而&#xff0c;在使用电脑的过程中&#xff0c;我们常常会遇到一些问题&#xff0c;其中之一就是软件运行时提示d3dx9_43.dll丢失。这个问题会导致软件游戏无法启动运行&#xff0c;但只要我们了解其原因和解…

LinkedHashMap、TreeMap

LinkedHashMap&#xff1a; 有序、不重复、无索引&#xff0c;底层是双链表 TreeMap&#xff1a;底层基于红黑树&#xff0c;可以对键进行排序 默认排序&#xff1a;integer和string都是从小到大排序 例题&#xff1a;

DP:子数组问题

文章目录 引言子数组问题介绍动态规划的基本概念具体问题的解决方法动态规划解法&#xff1a;关于子数组问题的几个题1.最大子数组和2.环形子数组的最大和3.乘积最大子数组4.乘积为正数的最长子数组长度5.等差数列划分 总结 引言 介绍动态规划&#xff08;DP&#xff09;在解决…

14分Top刊NC代码开源|NSCLC单细胞+空转肿瘤微环境分析

说在前面 说起肺癌真的过去回忆历历在目&#xff0c;小编毕业后职业生涯的第一个项目——非小细胞肺癌预后有效靶点筛选。当时肝的是转录组预后建模筛选。 做研发其实要求是远远高于发文章的&#xff0c;文章投不出去就降分&#xff0c;加工作量&#xff0c;做药要是烂尾或者…