数据结构——排序(4):归并排序+计数排序

news2024/12/28 3:37:30

目录

一、归并排序

(1)思想

(2)过程图示

(3)代码实现

 (4) 代码解释

(5)复杂度

二、非比较排序(计数排序)

(1)操作步骤

(2)图示

(3)思考

(4)代码实现

(5)注意 

(6)代码解释

(7)复杂度

三、排序性能对比

四、排序算法特性

稳定性验证案例

五、写在最后


一、归并排序

(1)思想

归并排序是建立在归并上的一种排序算法,该算法是采用分治法的一个典型应用。

将已有序的子序列合并,得到完全有序的序列(即先使每一个子序列有序,在使子序列段间有序)。若将两个有序表合并成一个有序表,称为二路归并

(2)过程图示

分解:类似于二叉树的结构,将子序列从中间分为左右序列[left , mid]、[mid + 1 , right],直至不能再分解。

合并:将两个子序列排好序合并在一起。

(3)代码实现

void _MergeSort(int* arr, int left, int right, int* tmp)
{
    if(left >= right)
    {
        return;
    }
    //分解
    int mid = (left + right) / 2;
    //左序列
    _MergeSort(arr, left, mid, tmp);
    //右序列
    _MergeSort(arr, mid + 1, right, tmp);

    //左序列
    int begin1 = left;
    int end1 = mid;
    //右序列
    int begin2 = mid + 1;
    int end2 = right;

    int index = begin1;
    //合并
    while(begin1 <= end1 && begin2 <= end2)
    {
        if(arr[begin1] < arr[begin2])
        {
            tmp[index++] = arr[begin1++];
        }
        else
        {
            tmp[index] = arr[begin2++];
        }
    }
    //begin1越界
    while(begin1 <= end1)
    {
        tmp[index++] = arr[begin1];
    }
    //或者begin2越界
    while(begin2 <= end2)
    {
        tmp[index++] = arr[begin2];
    }
    //根据tmp数组更新arr数组
    for(int i = left; i <= right; i ++)
    {
        arr[i] = tmp[i];
    }
}

void MergeSort(int* arr, int n)
{
    int* tmp = (int*)malloc(sizeof(int)*n);
    _MergeSort(arr, 0, n - 1, tmp);
    free(tmp);
}

 (4) 代码解释

1.首先,我们来看主函数MergeSort():创建一个与原数组等大小的数组tmp,然后进行归并排序。

2.接着,在_MergeSort中:

分解时,我们通过下标找到该序列的left和right,那么就可以找到左子序列[left , mid]和右子序列[mid + 1 , right],然后递归左右子序列,直至left>=right,结束递归。

合并时,我们创建变量begin1、end1、begin2、end2来表示左右子序列的两端,用index来表示tmp数组的下标。在两个序列中,我们将其中的数据进行排序。当跳出循环时,说明左子序列遍历完成或者右子序列遍历完成,将剩下的序列中的数据存放入tmp序列中即可。至此我们完成了合并。

3.完成了排序,此时排列好的数据仍然存放在tmp数组中,我们根据tmp将arr数组进行更新,到此就完成了整个归并排序。


(5)复杂度

 1.时间复杂度:O(N*logN);

2.空间复杂度:O(N)。


二、非比较排序(计数排序)

计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。

(1)操作步骤

1.统计出每个元素出现的次数;

2.将统计的结果保存在数组中。

(2)图示

例如:数组{6,1,2,9,4,2,4,1,1}中,1的个数为3,我们就让下标为1的位置存放3;2的个数为2,我们就让下标为2的位置存放2…… 

由此我们可以知道,我们申请空间的大小为数组中的最大值。

(3)思考

1.如果数组为{101,102,109,105,101,105},难道我们要申请109个空间吗?

就上述数组来说,如果我们申请了109个空间,不存在100之前的数据,那么那些空间不就浪费了吗?

因此单单按数据开辟空间是行不通的。

2.我们知道不存在为负数的下标,那如果数组中有负数怎么进行排序呢?

通过思考可能会想到将负数取绝对值,不就变成正数了吗?可是,如果负数是-5,同时数组中存在5,将负数取绝对值后,-5和5不就分不清了吗?因此这个方法也行不通。


为了不让空间浪费,并且解决负数排序的情况,我们可以开辟空间存放最小值和最大值之间的数据,即空间的大小为max - min + 1。这样,即使最小值为负数,我们让一个数减去最小的负数,结果必然是整数!


(4)代码实现

void CountSort(int* arr, int n)
{
    //找最大值和最小值
    int min = arr[0];
    int max = arr[0];
    for(int i = 1; i < n; i++)
    {
        if(arr[i] < min)
            min = arr[i];
        if(arr[i] > max)
            max = arr[i];
    }

    //确定新数组的大小
    int range = max - min + 1;
    //创建新数组
    int* count = (int*)malloc(sizeof(int) * n);
    if(count == NULL)
    {
        perror("malloc fail!");
        return;
    }

    //统计数组中各元素的个数
    for(int i = 0; i < n; i++)
    {
        count[arr[i] - min] ++;
    }

    //排序、输出
    int j = 0;
    for(int i = 0; i < range; i++)
    {
        while(count[i]--)
        {
            arr[j++] = i + min;
        }
    
    }
}

(5)注意 

1.统计数组中的元素时,数据arr[i]对应的新数组的下标为arr[i] - min;

2.在排序、输出时,数据arr[i]对应的新数组的数据为i + min。

(6)代码解释

首先我们找到要排序的数组的最大值和最小值,创建一个新数组保存统计的数据个数的结果,最后按照新数组的数据进行排序(更新原数组)。

(7)复杂度

1.时间复杂度:O(N + range);

2.空间复杂度:O(range)。

三、排序性能对比

//测试排序性能对比
void TestOP()
{
    srand(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 ++)
    {
        arr1[i] = rand();
        arr2[i] = arr1[i];
        arr3[i] = arr1[i];
        arr4[i] = arr1[i];
        arr5[i] = arr1[i];
        arr6[i] = arr1[i];
        arr7[i] = arr1[i];
        arr8[i] = arr1[i];
    }
    //直接插入排序
    int begin1 = clock();
    InsertSort(arr1,N);
    int end1 = clock();

    //希尔排序
    int begin2 = clock();
    ShellSort(arr1,N);
    int end2 = clock();

    //直接选择排序
    int begin3 = clock();
    SelectSort(arr1,N);
    int end3 = clock();

    //堆排序
    int begin4 = clock();
    HeapSort(arr1,N);
    int end4 = clock();

    //冒泡排序
    int begin5 = clock();
    BubbleSort(arr1,N);
    int end5 = clock();

    //快速排序
    int begin6 = clock();
    QuickSort(arr1,N);
    int end6 = clock();

    //归并排序
    int begin7 = clock();
    MergeSort(arr1,N);
    int end7 = clock();

    //计数排序
    int begin8 = clock();
    CountSort(arr1,N);
    int end8 = clock();

    printf("InsertSort:%d\n",end1 - begin1);
    printf("ShellSort:%d\n",end2 - begin2);
    printf("SelectSort:%d\n",end3 - begin3);
    printf("HeapSort:%d\n",end4 - begin4);
    printf("BubbleSort:%d\n",end5 - begin5);
    printf("QuickSort:%d\n",end6 - begin6);
    printf("MergeSort:%d\n",end7 - begin7);
    printf("CountSort:%d\n",end8 - begin8);

    free(arr1);
    free(arr2);
    free(arr3);
    free(arr4);
    free(arr5);
    free(arr6);
    free(arr7);
    free(arr8);
}

四、排序算法特性

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。 

 

稳定性验证案例

直接选择排序:58529;

希尔排序:58259;

堆排序:2222;

快速排序:53343891011。 

五、写在最后

至此我们学习了顺序表、链表、栈、队列、二叉树、排序,初阶数据结构完结~撒花!

我们C++见!!

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

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

相关文章

《新一代数据可视化分析工具应用指南》正式开放下载

2024年8月12日&#xff0c;由DataEase开源项目组编写的《新一代数据可视化分析工具应用指南》白皮书正式面向广大用户开放下载。 《新一代数据可视化分析工具应用指南》是DataEase开源项目组为了支持企业落地并推广BI工具、推进企业数据可视化建设而编著的指导手册。通过本白皮…

【生成式人工智能-十二-影像生成原理】

文字生成影像的原理 影像生成的应用影像 是怎么生成的图片生成图片&#xff1a;影像生成影像文字生成图片按照文字AT的方式生成NAT 文字生成视频 怎么评价影像生成的好坏引入CLIP模型文字生成图片的难点文字生成视频的难点解决办法减少attention数目分迭代生成 前段时间sora生成…

前端字体没有授权,字体版权检测(是否为方正字体)

1.截图系统中的首页和登录页面&#xff0c;主要是有字体的地方 2.在线字体版权检测地址&#xff1a;字体版权自动检测-求字体网 3.上传照片&#xff0c;开始对图片进行检测&#xff0c;每个账号有三次免费次数 4.检测完&#xff0c;直接查看检测报告即可&#xff0c; 报告中…

[Spring] Spring事务与事务的传播

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

借助 Aspose.Words,在 Word 文档中创建表格

Word 文档中的表格是一种强大的工具&#xff0c;可用于以清晰、结构化的格式组织和呈现数据。表格由行和列组成&#xff0c;行和列相交形成可包含文本、数字、图像或其他元素的单元格。在本文中&#xff0c;我们将学习如何使用 C# 以编程方式在 Word 文档中创建表格。本文通过代…

虚幻5|高级运动实现基础的翻滚Roll 基础篇

一&#xff0c;调整项目设置——输入 1.我设置了翻滚是同时按W&#xff0b;Shift按键&#xff0c;如果你有更好的按键安排&#xff0c;评论区留言 二&#xff0c;打开角色蓝图&#xff0c;打开图表—基础移动&#xff08;你的放图表就行了&#xff0c;我这里是安排了很多排版的…

LDRA Testbed(TBrun)软件单元测试_实例讲解(局部静态变量)

系列文章目录 LDRA Testbed软件静态分析_操作指南 LDRA Testbed软件静态分析_自动提取静态分析数据生成文档 LDRA Testbed软件静态分析_Jenkins持续集成&#xff08;自动静态分析并用邮件自动发送分析结果&#xff09; LDRA Testbed软件静态分析_软件质量度量 LDRA Testbed软件…

使用Hugging Face构建大型语言模型应用

在本文中&#xff0c;我们将介绍如何使用Hugging Face的大型语言模型&#xff08;LLM&#xff09;构建一些常见的应用&#xff0c;包括摘要&#xff08;Summarization&#xff09;、情感分析&#xff08;Sentiment analysis&#xff09;、翻译&#xff08;Translation&#xff…

初步融合snowboy+pyttsx3+espeak+sherpa-ncnn的python代码

在前文《将Snowboy语音唤醒的“叮”一声改成自定义语言》中&#xff0c;我已经实现唤醒snowboy后&#xff0c;树莓派会说一句自定义文本。今天&#xff0c;会在此基础上增加ASR的应用&#xff08;基于sherpa-ncnn&#xff09;。 首先&#xff0c;编写一个asr.py的程序&#xf…

手撕快排——三种实现方法(附动图及源码)

&#x1f916;&#x1f4bb;&#x1f468;‍&#x1f4bb;&#x1f469;‍&#x1f4bb;&#x1f31f;&#x1f680; &#x1f916;&#x1f31f; 欢迎降临张有志的未来科技实验室&#x1f916;&#x1f31f; 专栏&#xff1a;数据结构 &#x1f468;‍&#x1f4bb;&…

【C++】STL——list

前言 本篇博客我们接着来理解一个STL库里的list链表的结构&#xff0c;根据前面数据结构的铺垫&#xff0c;理解这个结构相对比较容易。我们来一起看看吧 &#x1f493; 个人主页&#xff1a;小张同学zkf ⏩ 文章专栏&#xff1a;C 若有问题 评论区见&#x1f4dd; &#x1f38…

中国与中南半岛国家多国语言系统开发i18n配置老挝、柬埔寨语言配置

前言 当下中国与中南半岛国家经济合作密切&#xff0c;同时也需要软件系统&#xff0c;多国使用系统需要实现多语言&#xff0c;我们团队最近也接到一个中、老、柬三国的业务软件&#xff0c;需要将软件做成三个国家语言。然后我们网上收i18n的老、柬的语言包命名&#xff0c;…

计算机毕业设计 美妆神域网站 美妆商城系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

j2:基于pytorch的resnet实验:鸟类分类

基于pytorch的resnet实验&#xff1a;鸟类分类 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 Ⅰ Ⅰ Ⅰ Introduction&#xff1a; 本文为机器学习使用resnet实现鸟类图片分类的实验&#xff0c;素材来自网…

跟李沐学AI:目标检测的常用算法

区域神经网络R-CNN 使用启发式搜索算法来选择锚框 -> 使用预训练模型来对每个锚框抽取特征 -> 训练一个SVM对类别进行分类 -> 训练一个线性回归模型来预测边缘框偏移 锚框大小不一&#xff0c;如何将不同的锚框统一为一个batch? -> 兴趣区域池化层 兴趣区域(RoI…

界面优化 - QSS

目录 1、背景介绍 2、基本语法 3、QSS 设置方式 3.1 指定控件样式设置 代码示例: 子元素受到影响 3.2 全局样式设置 代码示例: 使用全局样式 代码示例: 样式的层叠特性 代码示例: 样式的优先级 3.3 从文件加载样式表 代码示例: 从文件加载全局样式 3.4 使用 Qt Desi…

最新UI六零导航系统源码 | 多模版全开源

六零导航页 (LyLme Spage) 致力于简洁高效无广告的上网导航和搜索入口&#xff0c;支持后台添加链接、自定义搜索引擎&#xff0c;沉淀最具价值链接&#xff0c;全站无商业推广&#xff0c;简约而不简单。 使用PHPMySql&#xff0c;增加后台管理 多模板选择&#xff0c;支持在…

MySQL基础练习题46-每位经理的下属员工数量

目录 题目 准备数据 分析数据 总结 题目 我们将至少有一个其他员工需要向他汇报的员工&#xff0c;视为一个经理。 返回需要听取汇报的所有经理的 ID、名称、直接向该经理汇报的员工人数&#xff0c;以及这些员工的平均年龄&#xff0c;其中该平均年龄需要四舍五入到最接近…

【网络】IP分片与路径MTU发现

目录 MTU值 IP分片与重组 路径MTU发现 路径MTU发现原理 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 相关文章&#xff1a;【网络】从零认识IPv4-CSDN博客 MTU值 由于物理层的硬件限制&#xff0c;为了使网络性能最优&#xff0c;在数据链路层会有一个MTU值&#xff0…

算法【Java】—— 双指针算法

双指针算法 常见的双指针有对撞指针&#xff0c;快慢指针以及前后指针&#xff08;这个前后指针是指两个指针都是从从一个方向出发&#xff0c;去往另一个方法&#xff0c;也可以认为是小学学习过的两车并行&#xff0c;我也会叫做同向指针&#xff09;&#xff0c;在前后指针…