数据结构之排序补充

devtools/2024/11/8 16:00:15/

1. 非比较排序

上一篇文章我们罗列了数据结构中排序的八种方法。这八种方法都是需要比较才能实现的,那怎么样才可以通过非比较的方法来实现数组的排序呢?这里就提供一种非比较排序的方法。

具体的操作思路如下:

1. 先统计待比较数组arr中重复数组的个数,并相对应的记录下来。

2. 在另一个第二个新开辟的数组count里,将arr数组中的元素当作下标,将对应元素出现的个数作为元素对应放。

3. 然后遍历count数组里面的元素,如果元素不为零,就将这个数据的下标作为元素返回给原数组arr,将这个数据的元素作为返回次数继续返回。

4. 原数组arr此时就有序了。

 我们可以根据上面的思路看一下下面这个例子:

上面的数组为arr,下面的数组为count,先看arr数组中,6出现1次,1出现2次,2出现2次,9出现1次,4出现3次。所以在count数组中,下标为6的位置为1,下标为1的位置为2,下标为2的位置为2,下标为9的位置为1,下标为4的位置3。

 然后遍历count数组,下标为0没有元素,跳过,下标为1有2,那么就在arr数组中从下标为0开始,连续放两个1,然后继续遍历count数组,连续放两个2,三个4,1个6,1个9。就得到了新的arr数组1 1 2 2 4 4 4 6 9。

这种方法需要新开辟一个数组用于暂时存放数组,而这个新开辟的count数组大小与原arr数组中的最大元素是息息相关的。现在我们试着排下面这个数组:

由第一个例子我们不难知道,开辟count数组的空间就需要109+1=110(看上面的例子,要以元素中的最大值作为下标,就需要比最大值还要多一个空间大小,因为还有下标为0的位置)个整形大小的空间。而这个数组中最小值为100,意思是在tmp数组中,只有在下标为100的时候才会有元素出现,这样子的话前面99个整形大小的空间就被浪费了。并且如果数组里面的元素有负数的话,又不可能将这个负数的个数放在下标为负数的空间里。而我们利用计算机排序的数组中,这两种情况更多。所以我们可以采用下面这种优化的方法。

1. 同上,先将arr数组中的元素以及元素重复的个数对应的存储下来。数组的最大值为max,最小值为min。

2. 开辟max-min+1个空间作为count数组的空间大小。现在开辟的空间里面的值都是随机的,我们通过memset函数将空间里面的整型都初始化为0。

3. 假设arr数组中下标为i的arr[i]元素重复了n次,那么就在count数组中下标为arr[i]-min的位置定为n。

4. 当arr数组的情况全部放入count数组的后,遍历count数组,将count数组中元素不为零的下标为j的数据作为放回去的次数,值为i+min,从arr[0]开始依次放回去。

5. 此时arr数组的元素全部有序。

 现在我们继续以上面的示例数组{100,101,109,105,101,105}为例详细讲解一下这个过程:

首先原数组依然命名为arr,新开辟的数组为count。arr数组中最大值为109,最小值为100。所以开辟的count数组的大小为109-100+1=10个整型空间大小。100出现的次数为1次,101出现的次数为2次,109出现的个数为1次,105出现的个数为2次。所以在count数组中,下标为100-100=0的位置的数据为1,下标为101-100=1的位置的数据为2,下标为109-100=9的位置的数据为1,下标为105-100=5的位置的数据为2

然后遍历count数组,若数组元素为0的话直接跳过,若不为0,则将下标为j的元素count[j]作为上传次数,将j+min作为上传的数据,从arr[0]开始依次按顺序上传上去。连续放count[j]个大小为j+min的数据。最后就得到有序的数组arr{100,101,101,105,105,109}.

 通过这个过程,我们可以写一下代码:

//非比较排序
void CountSort(int* arr, int n)
{//先找数组arr中最大值max和最小值min,这里还是需要用到比较int min = arr[0];int max = arr[0];for (int i = 0; i < n; i++){if (arr[i] > max){max = arr[i];}if (arr[i] < min){min = arr[i];}}//开辟range个整型空间大小的数组countint range = max - min + 1;int* count = (int*)malloc(sizeof(int) * range);if (count == NULL){perror("malloc fail!");exit(1);}//将count数组里面的元素都初始化为0memset(count, 0, sizeof(int) * range);//将arr中下标为i的元素arr[j]-min作为下标传给count,将arr中元素出现的次数作为对应数值传给countfor (int i = 0; i < n; i++){count[arr[i] - min]++;}//将count中的下标j加上min作为数值传给arr,将count[j]作为连续的上传次数int index = 0;for (int j = 0; j < range; j++){while (count[j]--){arr[index++] = j + min;}}
}

 我们可以根据下面这个代码测试一下排序算法所需要的时间:

void test()
{srand((unsigned int)time(0));const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);int* a7 = (int*)malloc(sizeof(int) * N);int* a8 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; i++){a1[i] = rand();a2[i] =a1[i];a3[i] = a1[i];a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];a7[i] = a1[i];a8[i] = a1[i];}//测试冒泡排序int begin1 = clock();BubbleSort(a1, N);int end1 = clock();//测试直接插入排序int begin2 = clock();InsertSort(a2, N);int end2 = clock();//测试希尔排序int begin3 = clock();ShellSort(a3, N);int end3 = clock();//测试直接选择排序2int begin4 = clock();SelectSort2(a4, N);int end4 = clock();//测试快排hoare版本int begin5 = clock();QuickSort(a5, 0, N - 1);int end5 = clock();//测试非递归版本int begin6 = clock();QuickSortNonR(a6, 0, N - 1);int end6 = clock();//测试归并排序int begin7 = clock();MergeSort(a7, N);int end7 = clock();//测试非比较排序int begin8 = clock();CountSort(a8, N);int end8 = clock();printf("BubbleSort:%d\n", end1 - begin1);printf("InsertSort:%d\n", end2 - begin2);printf("ShellSort:%d\n", end3 - begin3);printf("SelectSort2:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("QuickSortNonR:%d\n", end6 - begin6);printf("MergeSort:%d\n", end7 - begin7);printf("CountSort:%d\n", end8 - begin8);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);free(a8);}

 代码的结果如下:

我们可以看到,冒泡排序的效率最低,快排第二,而非比较排序效率第一。所以说非比较排序在某些时候使用起来效率是最高的。但是有些时候不能使用非比较排序:

1. 很多数据不集中的时候不能使用非比较排序。

2.非比较排序不能排小数。

 2. 排序算法复杂度及稳定性分析

稳定性就是两个相同的数据在排序完成前和排序完成后的相对位置不改变的话就稳定,如果改变的话就不稳定。

 不稳定的四种情况可以通过下面四种情况进行实验即可得出结论:

直接选择排序:5 8 5 2 9 

希尔排序:5 8 2 5 9

堆排序:2 2 2 2

快速排序:5 3 3 4 3 8 9 10 11


http://www.ppmy.cn/devtools/132349.html

相关文章

AIDOVECL数据集:包含超过15000张AI生成的车辆图像数据集,目的解决旨在解决眼水平分类和定位问题。

2024-11-01&#xff0c;由伊利诺伊大学厄巴纳-香槟分校的研究团队创建的AIDOVECL数据集&#xff0c;通过AI生成的车辆图像&#xff0c;显著减少了手动标注工作&#xff0c;为自动驾驶、城市规划和环境监测等领域提供了丰富的眼水平车辆图像资源。 数据集地址&#xff1a;AIDOV…

el-table 表格索引不展示问题

问题&#xff1a;el-table&#xff0c;前端将dom结构传给后端&#xff0c;在另一个页面获取这个dom&#xff0c;渲染&#xff0c;一开始&#xff0c;列表样式全部挤到一起&#xff0c;样式错乱 若表格有初始化是隐藏的&#xff0c;需要事件点击显示的 则表格索引会消失 .loo…

数据结构 ——— 查找链式二叉树中值为X的节点

目录 链式二叉树示意图 手搓一个链式二叉树 查找链式二叉树中值为X的节点 链式二叉树示意图 手搓一个链式二叉树 代码演示&#xff1a; // 数据类型 typedef int BTDataType;// 二叉树节点的结构 typedef struct BinaryTreeNode {BTDataType data; //每个节点的数据struc…

轻型民用无人驾驶航空器安全操控------理论考试多旋翼部分笔记

官网&#xff1a;民用无人驾驶航空器综合管理平台 (caac.gov.cn) 说明&#xff1a;一是法规部分&#xff1b;二是多旋翼部分 本笔记全部来源于轻型民用无人驾驶航空器安全操控视频讲解平台 目录 官网&#xff1a;民用无人驾驶航空器综合管理平台 (caac.gov.cn) 一、轻型民用无人…

计算机网络:网络层 —— 多播路由选择协议

文章目录 多播路由选择协议多播转发树构建多播转发树基于源树的多播路由选择建立广播转发树建立多播转发树 组共享树的多播路由选择基于核心的生成树的建立过程 因特网的多播路由选择协议 多播路由选择协议 仅使用 IGMP 并不能在因特网上进行IP多播。连接在局域网上的多播路由…

《深入浅出HTTPS​​​​》读书笔记(5):随机数

密码学中随机数的用途非常大&#xff0c;其他密码学算法内部都会用到随机数。 1&#xff09;效率 在软件或者密码学应用中需要大量的随机数&#xff0c;必须在很短的时间内生成随机数。 2&#xff09;随机性 生成的随机数只要不存在统计学偏差&#xff0c;那么这个随机数就具备…

飞腾平台Arm ComputeLibrary编译安装指南

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…

100+SCI科研绘图系列教程(R和python)

科研绘图系列&#xff1a;箱线图加百分比点图展示组间差异-CSDN博客科研绘图系列&#xff1a;箱线图加蜜蜂图展示组间数据分布-CSDN博客科研绘图系列&#xff1a;小提琴图和双侧小提琴图展示组间差异-CSDN博客科研绘图系列&#xff1a;组间差异的STAMP图的ggplot2实现-CSDN博客…