决战排序之巅(一)

决战排序之巅

    • 插入排序
      • 直接插入排序 void InsertSort(int* arr, int n)
      • 希尔排序 void ShellSort(int* arr, int n)
      • 测试插入排序
        • 测试函数 void verify(int* arr, int n)
        • 测试 InsertSort
        • 测试 ShellSort
        • 测试速度 InsertSort & ShellSort
    • 选择排序
      • 直接选择排序 void SelectSort(int* arr,int n)
      • 堆排序 void HeapSort(int* arr,int n)
        • 堆向下调整 void HeapDown(int* arr, int father,int size)
        • 堆排序 void HeapSort(int* arr,int n)
      • 测试选择排序
        • 测试 SelectSort
        • 测试 HeapSort
        • 测试速度 SelectSort & HeapSort
    • 希尔 VS 堆排 (Debug版本)
      • 说明
      • 1w rand( ) 数据测试
      • 10w rand( ) 数据测试
      • 10w rand( ) + i 数据测试
      • 100w rand( ) 数据测试
      • 100w rand( ) + i 数据测试
      • 1000w rand( ) 数据测试
      • 1000w rand( ) + i 数据测试
      • 测试代码如下:
    • 结语

欢迎来到决战排序之巅栏目,
本期我们将带来 插入排序(希尔) 与 选择排序(堆排) 的实现与比较

请添加图片描述
排序要常用的Swap函数(交换两个数值)

void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}

插入排序

直接插入排序 void InsertSort(int* arr, int n)

基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。
插入流程如下所示:

当插入第i (i>=1)个元素时,前面的arr[0],arr[1],…,arr[i-1]已经排好序,此时用arr[i]的排序码arr[i-1],arr[i-2],…的排序码顺序进行比较,找到插入位置即将arr[i]插入,原来位置上的元素顺序后移。
代码如下:

void InsertSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++){int end = i;while (end >= 0){if (arr[end + 1] < arr[end]){Swap(&arr[end + 1], &arr[end]);end--;}else{break;}}}
}

直接插入排序分析
特性:元素集合越接近与有序,直接插入排序算法的时间效率越高。
时间复杂度:O(N^2)
空间复杂度:O(N)
稳定性:稳定

希尔排序 void ShellSort(int* arr, int n)

希尔排序法又称缩小增量法
希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后让堆gap重新取值,重复上述分组和排序的工作。当到达gap==1时,所有记录在统一组内排好序。
在这里插入图片描述
代码如下:

void ShellSort(int* arr, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++){int end = i;while (end >= 0){if (arr[end + gap] < arr[end]){Swap(&arr[end + gap], &arr[end]);end -= gap;}else{break;}}}}
}

希尔排序分析:
1.希尔排序是对直接插入排序的优化。
2.但gap > 1是程序对进行预排序,目的是使数组逐渐趋向于有序化,。当gap==1时数组就已经接近有序了,便可以很快的排好。对于整体而言,这样可以达到优化的效果。
3.希尔排序的gap取值有很多种取法,例如,最初Shell所提出的gap = [n/2] , gap = [gap/2],还有后来Knuth所提出的gap = [gap/3] + 1,还有人提出都取奇数,也有人提出各gap互质。但没有一种主张得到证明,因为Shell排序的时间度分析极其困难。在Knuth所著的**《计算机程序设计技巧》中利用大量试验资料得出,当n很大时,关键码平均比较次数和对象平均移动次数大约在 [ n 1.25 , 1.6 n 1.25 ] [ n^ {1.25} , 1.6n^{1.25}] [n1.25,1.6n1.25]范围内,这是 利用直接插入排序作为子序列方法 的情况下得到的。而我们以上代码的gap就是按照Knuth**提出的方式取值的。
稳定性:不稳定

测试插入排序

测试函数 void verify(int* arr, int n)
void verify(int* arr, int n)
{for (int i = 1; i < n; i++){assert(arr[i] >= arr[i - 1]);}
}

以排非降序为例,若全为非降序则程序顺利通过,否则由assert函数终止程序并告知有误。

测试 InsertSort

我们先利用malloc开辟一个可存储10000个int类型的数组,再利用循环将数组内的数全置为随机数,再进行排序并检验。
在这里插入图片描述

我们运行后可以看到程序顺利通过,这说明测试成功,InsertSort正确无误。

测试 ShellSort

同理测试ShellSort.
在这里插入图片描述

可以看到ShellSort也是正确无误的。
测试代码:

void test_Sort()
{int n = 10000;int* arr = (int*)malloc(sizeof(int) * n); assert(arr);for (int i = 0; i < n; i++){arr[i] = rand();}ShellSort(arr, n);verify(arr, n);
}int main()
{srand((unsigned int)time(NULL));test_Sort();return 0;
}
测试速度 InsertSort & ShellSort

先写一个numcreate函数来开辟空间。

int* numcreate(int n)
{int* arr = (int*)malloc(sizeof(int) * n);assert(arr);return arr;
}

开辟两个可储存10w int类型的数组,并利用rand( )函数为他们附上相同的值,再利用clock()函数来记录时间,最后比较即可。
在这里插入图片描述
我们可以看到插入排序用了5512μs,而希尔排序只用了13μs,所以恭喜ShellSort在速度上战胜了InsertSort,代码如下:

test()
{int n = 100000;int* arr1 = numcreate(n);int* arr2 = numcreate(n);for (int i = 0; i < n; i++){arr2[i] = arr1[i] = rand();}int begin1 = clock();InsertSort(arr1, n);int end1 = clock();int begin2 = clock();ShellSort(arr2, n);int end2 = clock();printf("Insertsort : %d\n", end1 - begin1);printf("ShellSort  : %d\n", end2 - begin2);free(arr1);free(arr2);
}

选择排序

直接选择排序 void SelectSort(int* arr,int n)

基本思想:每次从待排数据中选出最小(最大)的值,再将其与起始位置的值交换,如此反复直到待排数据排完为止。
优化思路:每次选出最大值和最小值,最大值与待排数据末尾交换,最小值与待排数据起始位置交换,再反复循环即可。

实现步骤:
1.先确定数据开始位置begin与结束位置end
2.利用for循环找到[begin,end]区间的最大最小值,再分别交换,之后更新beginend
3.利用while循环来判断待排数据完成的条件
4.需要注意的是:当最大值为begin时,我们在交换时先交换了minibegin位置的数据,所以在进行maxiend前,我们要对maxi重新赋值,因为最大值被交换到了mini的位置,所以要maxi = mini

void SelectSort(int* a,int n)
{int begin=0,end=n-1;while(begin<end){int maxi=begin,mini=end;for(int i=begin;i<=end;i++){if(a[maxi]<a[i]){maxi=i;}if(a[mini]>a[i]){mini=i;}}Swap(&a[begin],&a[mini]);if(begin==maxi) {maxi=mini;}Swap(&a[end],&a[maxi]);end--;begin++;}
}

直接选择排序分析:
特性:思路通俗易懂,但效率不高,且实际应用不高
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不稳定

堆排序 void HeapSort(int* arr,int n)

概念:堆排序是利用堆这种数据结构所设计的一种算法结构,通过逐个比较自身节点与左右子节点的大小来进行选择排序,这是选择排序的一种,它是通过堆来进行选择数据的。
方法:排升序建大堆,排降序建小堆。(本篇文章以排升序为例)
代码如下:

堆向下调整 void HeapDown(int* arr, int father,int size)

这里的size表示要调整数组的结束下标,father代表父节点即开始调整的位置,arr代表要调整的数组。

void HeapDown(int* arr, int father,int size)
{int child = father * 2 + 1;while (child < size){if (child + 1 < size && arr[child + 1] > arr[child]){child++;}if (arr[father] < arr[child]){Swap(&arr[father], &arr[child]);father = child;child = child * 2 + 1;}else{break;}}
}

先选出最大的子节点,在与父亲进行比较,若父节点小于子节点则进行交换,直到子节点要小于父节点的值,或者child>=size即子节点的下标值大于size结束下标的值就跳出循环。

堆排序 void HeapSort(int* arr,int n)

利用大堆的特性,堆顶一定为堆中的最大值,所以我们可以利用循环取出堆顶与堆中的最后一个数进行交换,在向下调整堆中 0 ~ n-1-i的数据位置,使得堆顶又重新变成下标为0~n-1-i 时的最大值,在依次循环,最后就排好了一个升序。
代码如下:

void HeapSort(int* arr, int n)
{int i = 0;for (i = (n - 1 - 1) / 2 ; i >=0 ; i--){HeapDown(arr, i, n);}//建堆for (i = 0; i < n - 1 ; i++){Swap(&arr[0], &arr[n - i - 1]);HeapDown(arr, 0, n - i - 1);}//排序
}

堆排序:
特点:堆排序利用堆来选择数据进行排序,这样效率就快很多了。
时间复杂度:O(N*logN)
空间复杂度:O(1)
稳定性:不稳定

测试选择排序

测试 SelectSort

相同的方法测试10w个 数据,成功。

测试 HeapSort

在这里插入图片描述
相同的方法测试100w个 数据,成功。

void test_Sort()
{int n = 1000000;int* arr = (int*)malloc(sizeof(int) * n); for (int i = 0; i < n; i++){arr[i] = rand();}HeapSort(arr, n);verify(arr, n);
}int main()
{srand((unsigned int)time(NULL));test_Sort();return 0;
}
测试速度 SelectSort & HeapSort

希尔 VS 堆排 (Debug版本)

说明

以下会分别对1w,10w,100w,1000w的数据进行100次的排序比较,并计算出排一趟的平均值。

rand( ) 生成随机数:rand( )函数生成的随机数区间为[0 , 32767] , rand()在10w以上量级的数据中会有较多的重复项。
rand( ) + i 生成随机数:它可以有效地避免rand( )在10w以上量级生成区间的问题,但是随着 i 越大,它生成的整体来看是较为有序的。

介绍就到这里了,让我们来看看这100次排序中,谁才是你心目中的排序呢?
PS:100次只是一个小小的测试数据,有兴趣的朋友可以在自己电脑上测试更多的来比较哦。

1w rand( ) 数据测试

在这里插入图片描述

10w rand( ) 数据测试

在这里插入图片描述

10w rand( ) + i 数据测试

在这里插入图片描述

100w rand( ) 数据测试

在这里插入图片描述

100w rand( ) + i 数据测试

在这里插入图片描述

1000w rand( ) 数据测试

在这里插入图片描述

1000w rand( ) + i 数据测试

在这里插入图片描述

测试代码如下:

int* numcreate(int n)
{int* arr = (int*)malloc(sizeof(int) * n);assert(arr);return arr;
}void Ultimate_Test()
{int n = 10000000, count = 100;int timeShell = 0, timeHeap = 0;for (int a = 0; a < count; a++){int* arr1 = numcreate(n);int* arr2 = numcreate(n);for (int i = 0; i < n; i++) arr1[i] = arr2[i] = rand() + i;int begin1 = clock();ShellSort(arr1, n);int end1 = clock();int begin2 = clock();HeapSort(arr2, n);int end2 = clock();timeShell += end1 - begin1;timeHeap += end2 - begin2;free(arr1);free(arr2);}printf("ShellSort : %.2f\n", 1.0 * timeShell / count);printf("HeapSort  : %.2f\n", 1.0 * timeHeap / count);}int main()
{srand((unsigned int)time(NULL));Ultimate_Test();return 0;
}

结语

看完之后,谁才是你心目中的排序呢?
欢迎留言,让我们一起来期待在下一期 《决战排序之巅(二)》

以上就是本期的全部内容喜欢请多多关注吧!!!

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

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

相关文章

前端带你学后端系列 ①【RocketMQ】

前端带你学后端系列 ①【RocketMQ】 Ⅰ 我们为什么要用RocketMQ&#xff1f;这个中间件有啥作用&#xff1f;Ⅱ RocketMQ 的组成元素Ⅲ RocketMQ 的系统架构Ⅳ 消息是怎么发送的&#xff1f;又是怎么存储的&#xff1f;又是如何拉取的&#xff1f;消息发送消息存储消息拉取 Ⅴ …

为什么FPGA是战略芯片?

FPGA&#xff08;Field Programmable Gate Array&#xff09;是在PAL&#xff08;可编程阵列逻辑&#xff09;、GAL&#xff08;通用阵列逻辑&#xff09;等可编程器件的基础上进一步发展的产物&#xff0c;它是作为一种半定制电路而出现的&#xff0c;既解决了定制电路的不足&…

STM32-02-STM32基础知识

文章目录 STM32基础知识1. STM32F103系统架构2. STM32寻址范围3. 存储器映射4. 寄存器映射 STM32基础知识 1. STM32F103系统架构 STM32F103 STM32F103是ST公司基于ARM授权Cortex M3内核而设计的一款芯片&#xff0c;而Cortex M内核使用的是ARM v7-M架构&#xff0c;是为了替代…

14:00面试,14:08就出来了,问的问题有点变态。。。。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到5月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

React antd如何实现<Upload>组件上传附件再次上传已清除附件缓存问题

最近遇到一个React上传组件的问题&#xff0c;即上传附件成功后&#xff0c;文件展示处仍然还有之前上传附件的缓存信息&#xff0c;需要解决的问题是&#xff0c;要把上一次上传的附件缓存在上传成功或者取消后&#xff0c;可以进行清除 经过一顿试错&#xff0c;终于解决了这…

搭建个人智能家居 开篇(搭建Home Assistant)

搭建个人智能家居 开篇&#xff08;搭建Home Assistant&#xff09; 前言Home Assistant搭建Home AssistantUbuntu系统搭建Windows系统搭建VM安装方法VirtualBox安装方法&#xff1a; 配置Home Assistant控制页面 前言 随着科技的进步、发展&#xff0c;物联网给我们的生活带来…

企业计算机服务器中了360勒索病毒如何解密,勒索病毒解密数据恢复

网络技术的不断应用与发展&#xff0c;为企业的生产运营提供了极大便利&#xff0c;但网络安全一直存在&#xff0c;网络勒索病毒的加密与攻击技术也在不断增加。近期&#xff0c;云天数据恢复中心陆续接到很多企业的求助&#xff0c;企业的计算机服务器遭到了360勒索病毒攻击&…

(十五)Flask覆写wsgi_app函数实现自定义中间件

中间件 一、剖析&#xff1a; 在前面讲session部分提到过&#xff1a;请求一进来&#xff0c;Flask会自动调用应用程序对象【Flask(__name__)】的__call__方法&#xff0c;这个方法负责处理请求并返回响应&#xff08;其实如下图&#xff1a;其内部就是wsgi_app方法&#xff…

【抄作业】ImportError :cannot import name xxxxxx ,原博主Activewaste

前情介绍 网上关于这种问题的解决方案一大堆&#xff0c;但是绝大多数都是不适用&#xff0c;或者说解决不了问题&#xff0c;我根据别人所遇到的和我自己遇到的&#xff0c;对这个问题整理了一下&#xff0c;希望能解决这个问题。 问题分析 一、缺少这个module或者func或者p…

【C++】C++中的String类详解及模拟实现示例

文章目录 string类简介string类的基本用法string类的常用方法string类的优势 string类的模拟实现存储结构头文件string.h源文件string.cpp源文件test.cpp string类简介 string类简介在C编程中&#xff0c;字符串是一种非常常见的数据类型&#xff0c;用于存储文本信息。C标准库…

我的网站服务器被入侵了该怎么办?

最近有用户咨询到德迅云安全&#xff0c;说自己再用的网站服务器遇到了入侵情况&#xff0c;询问该怎么处理入侵问题&#xff0c;有什么安全方案可以解决服务器被入侵的问题。下面&#xff0c;我们就来简单讲下服务器遇到入侵了&#xff0c;该从哪方面入手处理&#xff0c;在预…

全球化需要先搬离中国?中国公司出海不应失去“模式自信”

中国企业出海近期热闹非凡&#xff0c;其中以短剧为代表的文化内容产业和跨境电商产业都吸引了大量关注。例如亚马逊在12月12日公布一组最新数据&#xff0c;亚马逊过去一年销售额超过1000万美金的中国卖家数量&#xff0c;同比增长接近30%。中国跨境电商平台在刚刚过去的“黑五…

机器学习算法应用场景与评价指标

一、应用场景 机器学习的算法选择大部分依赖于具体的问题类型和数据特征。下面是一些典型的场景以及对应的常用算法&#xff1a; 1.二元分类问题 当你的目标变量只有两个类别时&#xff0c;如垃圾邮件过滤&#xff08;垃圾邮件/非垃圾邮件&#xff09;、患者疾病诊断&#x…

医院污水处理设备远程监控超标报警解决方案

行业背景 近年来&#xff0c;我国医疗机构建设得到了巨大的发展。根据《2022年我国卫生健康事业发展统计公报》&#xff0c;2022年末&#xff0c;全国医疗卫生机构总数达1032918个。截至2022年10月&#xff0c;根据全国排污许可证管理信息平台&#xff0c;共有 13316家医院核发…

UE引擎 LandscapeGrass 实现机制分析(UE5.2)

前言 随着电脑和手机硬件性能越来越高&#xff0c;游戏越来越追求大世界&#xff0c;而大世界非常核心的一环是植被&#xff0c;目前UE5引擎提供给植被生成的主流两种方式为 手刷植被和LandscapeGrass(WeightMap程序化植被)。当然UE5.3推出新一代PCGFramework 节点程序化生成框…

Halcon reduce_domain和scale_image的作用

在Halcon中&#xff0c;reduce_domain是用于缩小图像域&#xff08;Image Domain&#xff09;的操作。 它的作用是通过指定一个感兴趣区域&#xff08;ROI&#xff0c;Region of Interest&#xff09;&#xff0c;将图像数据限制在该区域内&#xff0c;从而实现对图像进行裁剪…

领导力的3个常见误区,你可能中了其中之一

什么是领导力&#xff1f; 领导力是组织和团队成功的关键。在一个不断变化的商业环境中&#xff0c;理解领导力的本质至关重要。这篇文章将揭示有关领导力的三个常见误解&#xff0c;帮助读者更清晰地认识领导者的角色。 关于领导力的常见误解 人们对领导力的理解经常受到错…

如何使用bash写脚本

本章主要介绍如何使用bash写脚本。 了解通配符了解变量了解返回值和数值运算数值的对比判断语句循环语句 grep的用法是“grep 关键字 file”&#xff0c;意思是从file中过滤出含有关键字的行。 例如&#xff0c;grep root /var/log/messages&#xff0c;意思是从/var/log/me…

在做题中学习(33):只出现一次的数字 II

137. 只出现一次的数字 II - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 1.首先想到出现三次的数&#xff0c;它们仨的任意一位都是相同的&#xff08;1/0&#xff09; 2.可以发现出现三次的数的某一位和a某一位在所有情况下%3最后的结果都和a的那一位相同&…

【PyTorch】卷积神经网络

文章目录 1. 理论介绍1.1. 从全连接层到卷积层1.1.1. 背景1.1.2. 从全连接层推导出卷积层 1.2. 卷积层1.2.1. 图像卷积1.2.2. 填充和步幅1.2.3. 多通道 1.3. 池化层&#xff08;又称汇聚层&#xff09;1.3.1. 背景1.3.2. 池化运算1.3.3. 填充和步幅1.3.4. 多通道 1.4. 卷积神经…