C++算法:排序之二(归并、希尔、选择排序)

news2024/11/17 17:28:29

C++算法:排序

排序之一(插入、冒泡、快速排序)
排序之二(归并、希尔、选择排序)


文章目录

  • C++算法:排序
  • 二、比较排序算法实现
    • 4、归并排序
    • 5、希尔排序
    • 5、选择排序
  • 原创文章,未经许可,严禁转载


本文续:C++算法:排序之一(插入、冒泡、快速排序)


二、比较排序算法实现

4、归并排序

归并排序和前面介绍的三种排序方法不同,这是一个以空间换时间的排序法。归并就是将二个或多个已排序的的序列合并成一个,常用的就是二路归并,所以它的空间复杂度很高。

所以归并排序适用于数据量大,并且对稳定性有要求的场景。它也适用于链表排序,可以在O(nlogn)时间内排序。此外,归并排序多用于需要外部排序的场景,比如磁盘文件的情况下比快排好,因为快排很依赖数据的随机存取,而归并是顺序存取,对磁盘这种外存比较友好。
在这里插入图片描述

代码如下(示例):

#include <iostream>
#include <vector>

using namespace std;

void merge(vector<int> &arr1, vector<int> &arr2, vector<int> &merg){
    int len1 = arr1.size(), len2 = arr2.size();
    int i = 0, j = 0, m = 0;
    while (m < len1 + len2){
        if (i == len1){
            while (j < len2){
                merg[m] = arr2[j];
                j++;
                m++;
            }
            break;
        }
        if (j == len2){
            while (i < len1){
                merg[m] = arr1[i];
                i++;
                m++;
            }
            break;
        }
        if (arr1[i] < arr2[j]){
            merg[m] = arr1[i];
            i++;
            m++;
        } else{
            merg[m] = arr2[j];
            j++;
            m++;
        }
    }
}

void merge_sort(vector<int> &vec){
    int n = vec.size();
    if (n > 1){
        int i = n/2;
        int j = n - n/2;
        vector<int> arr1;
        vector<int> arr2;
        for (int k=0; k<i; k++){
            arr1.push_back(vec[k]);
        }
        for (int k=i; k<j+i; k++){
            arr2.push_back(vec[k]);
        }
        merge_sort(arr1); 
        merge_sort(arr2); 
        merge(arr1, arr2, vec); 
    }
}

int main(){
    vector<int> vec = {6, 5, 3, 1, 8, 7, 2, 4};
    merge_sort(vec);
    for (auto it=vec.begin(); it!=vec.end(); it++){
        cout << *it << " ";
    }
    return 0;
}

归并排序也是一种遵循分治算法思想的排序方法,它将一个大数组分成两个小数组,对每个小数组进行排序,然后将两个已排序的小数组合并成一个有序的大数组。

在这段代码中,merge_sort 函数是归并排序的主要函数。首先,它检查 vec 的大小是否大于1。如果 vec 的大小小于等于1,则不需要排序,直接返回。否则,将 vec 分成两个部分:arr1 和 arr2。然后,对这两个部分分别调用 merge_sort 函数进行排序。最后,调用 merge 函数将这两个已排序的部分合并成一个有序的大数组。

merge 函数接受三个 vector 类型的引用作为参数:arr1,arr2 和 merg。它将两个已排序的数组 arr1 和 arr2 合并成一个有序的大数组 merg。函数使用三个变量 i,j 和 m 来跟踪三个数组中的位置。当 m < len1 + len2 时(其中 len1 = arr1.size(),len2 = arr2.size()),循环执行以下操作:

  • 如果 i == len1,则将 arr2 中剩余的元素复制到 merg 中,并退出循环。
  • 如果 j == len2,则将 arr1 中剩余的元素复制到 merg 中,并退出循环。
  • 如果 arr1[i] < arr2[j],则将 arr1[i] 复制到 merg[m] 中,并增加 i 和 m 的值。
  • 否则,将 arr2[j] 复制到 merg[m] 中,并增加 j 和 m 的值。

这里要注意 i、j 是否等于对应长度的检查,应及时用break退出循环,不然可能引起段错误。归并排序虽然代码有点长,但并不难以理解,逻辑是很简单清晰的。

5、希尔排序

希尔是个人名,这是以人名命名的一个排序算法。这是一种很奇特的排序方法,也难怪发明者可以以此留名。它的时间复杂度在比较排序中是较好的,还是就地排序,不额外占用空间。可惜不能对链表结构排序。

希尔排序是一种基于插入排序的算法,它比插入排序和选择排序要快得多,并且数组越大,优势越大。它适用于大型的数组。排序的时间复杂度会比O(n^2)好,但没有快速排序算法快O(n(logn)),在中等大小规模数组表现良好,总之不怎么常用。

在这里插入图片描述
希尔排序也称为缩小增量排序。它的基本原理是将待排序的数组元素按下标的一定增量分组,分成多个子序列,然后对各个子序列进行直接插入排序算法排序;然后依次缩减增量再进行排序,直到增量为1时,进行最后一次直接插入排序,排序结束。

上图的分组过程表现得不是很直观,下方的过程解释和代码是严格匹配这个分组过程的。

  • 第一遍是分成两个组,gap是增量的意思,总共10个元素,第一遍gap就是5,从0到5这5个元素为1 组,5到10为一组,对这两组进行插入排序。它其实是将两组同样位置的元素比较(第一次是0和5比较),如果1组同位置元素大于2组的,则交换并继续向前以gap差比较。图中同颜色的元素就是比较过程的演示。0:5,1:6,2:7 …
  • 第二遍gap又除以2,gap=2。分成了5组,这样就成了:2组比1组的同位置元素,3组比2组的同位置元素并继续和1组的同位置元素比较,…
  • 第三遍gap就等于1了,那就是一个插入排序过程。

代码如下(示例):

#include <iostream>
#include <vector>

using namespace std;

void shell_sort(vector<int> &vec){
    int len = vec.size();
    int insert_num = 0;
    int gap = len / 2;      //初始化增量
    while (gap){           //当增量大于等于1时,执行循环
        for (int i = gap; i < len; ++i){       //对每个子序列进行插入排序
            insert_num = vec[i];
            int j = i;
//如果当前元素小于前一个元素,则交换位置, 其实就是插入排序,只是指针设计得特殊
            while (j >= gap && insert_num < vec[j - gap]){   
                vec[j] = vec[j - gap];
                j -= gap;
            }
            vec[j] = insert_num;  //j已经减了gap
        }
        gap = gap / 2; // 减小增量
    }
}

int main(){
    vector<int> vec = {83, 84, 88, 87, 61, 50, 70, 60, 80, 99};
    shell_sort(vec);
    for (auto it=vec.begin(); it!=vec.end(); it++){
        cout << *it << " ";
    }
    return 0;
}

首先,我们初始化增量 gap 为序列长度的一半。然后,当 gap 大于等于1时,执行循环。在循环中,我们对每个子序列进行插入排序。如果当前元素小于前一个元素,则交换位置。最后,我们减小增量 gap 的值,并重复执行上述操作。当 gap 的值为1时,整个序列就是有序的。在本例中,子序列其实就是插入排序,只是指针设计得特殊,也有使用冒泡排序来进行子序列排序的。

5、选择排序

选择排序是最直观的排序算法了,每次循环拿走最大的元素放在最后位置。循环 n-1 次后,排序就完成了,当然每次取最小元素放前面也是一样的,小学生就能搞明白的算法。
在这里插入图片描述

代码如下(示例):

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void select_sort(vector<int> &vec){
    int len = vec.size();
    int idx = 0, mini;
    while (idx < len){
        mini = vec[idx];
        for (int i=idx+1; i<len ;++i){
            if (vec[i] < mini){
                mini = vec[i];
            }
        }
        swap(mini, vec[idx]);
        idx++;
    }  
}

int main(){
    vector<int> vec = {2,3,4,5,15,19,26,27,36,38,44,46,47,48,50};
    select_sort(vec);
    for (auto it=vec.begin(); it!=vec.end(); it++){
        cout << *it << " ";
    }
    return 0;
}
  • 首先,初始化 idx 为 0,表示当前排序序列的起始位置。
  • 然后,在未排序序列中找到最小元素赋值 mini。
  • 接着,交换 mini 和 vec[idx] 的值,将最小元素放到排序序列的起始位置。
  • 然后,将 idx 加 1,表示已排序序列的末尾位置。
  • 重复步骤 2-4,直到所有元素均排序完毕。

选择排序在以特定顺序排序时是有用的,就是并不是以大小排序的情况。如CSDN每日一练中有一题任务分配问题,就要用到这种排序方式。当然实际上这种排序方法几乎没用,除了应付考试。


未完待续…

原创文章,未经许可,严禁转载

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

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

相关文章

从vue2到vue3的生命周期

1.vue2 在vue2.x中的生命周期为 beforeCreate created beforeMount mounted beforeUpdate updated beforeDestroy destroyed activated deactivated errorCaptured 在vue3中&#xff0c;新增了一个setup生命周期函数&#xff0c;setup执行的时机是在beforeCreate生命函数之前…

count(0)、count(1)和count(*)、count(列名) 的区别

当我们对一张数据表中的记录进行统计的时候&#xff0c;习惯都会使用 count 函数来统计&#xff0c;但是 count 函数传入的参数有很多种&#xff0c;比如 count(1)、count(*)、count(字段) 等。 到底哪种效率是最好的呢&#xff1f;是不是 count(*) 效率最差&#xff1f; 一.…

【Mysql数据库从0到1】-入门基础篇--sql语句简单使用

【Mysql数据库从0到1】-入门基础篇--sql语句简单使用 &#x1f53b;一、数据库创建、删除、选择1.1 &#x1f343; create database 创建数据库1.2 &#x1f343; 使用 mysqladmin 创建数据库1.3 &#x1f343; drop 命令删除数据库--一般不建议在数据库执行delete、drop等命令…

公司招人面试了一个00后,绝对能称为是内卷届的天花板

公司前段缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资也不低&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。令我印象最深的是一个00后测试员&#xff0c;他…

【商品页面详情页+商品评论】API接口技术交流,封装接口

商品详情API接口数据&#xff1a;提供了商品的基本信息&#xff0c;包括商品名称、描述、规格、价格、销量、库存等信息。此外&#xff0c;也可以通过提供的API接口来获取商品的图片、评价、物流信息等详细数据。 商品评论接口是消费者对商品所进行的客观评价 电商API的应用价…

使用geoserver发布shp和tiff数据

一、安装并启动geoserver服务 1.1 下载geoserver 进入官网下载 由于geoserver是使用Java语言开发的&#xff0c;所以运行需要java的环境&#xff0c;不同geoserver的版本号对java的版本要求不同&#xff0c;所以选择版本时需注意对应java的版本要求&#xff0c;由于我本地安…

Nginx配置域名证书

Nginx配置域名证书 1、证书存放路径 2、nginx.conf文件中增加以下配置&#xff0c;注意路径不一样&#xff0c;访问地址目录不一样 server {listen 443 ssl http2;server_name jistest.vwatj.ap.vwg;ssl_certificate D:/home/XXX/ssl/2023/XXX.cer; ssl_certificate_key D…

Spring Validation 接口入参校验

一、前言 JSR 是 Java Specification Requests 的缩写&#xff0c;含义为 JAVA 规范提案。 JSR 303 - Bean Validation 规范, 正是一套基于 JavaBean 参数校验的标准。 Hibernate Validator 是 JSR 303 的实现&#xff0c;它提供了 JSR 303 规范中所有约束&#xff08;constrai…

泪崩!测试面试技术面过了却挂在了——“谈谈你的职业生涯规划”

前不久&#xff0c;软件测试交流群里面有一个成员吐槽&#xff0c;说今天的面试技术已经面过了&#xff0c;可HR却问了她“未来的职业发展目标是什么&#xff1f;”然后&#xff0c;挂了&#xff01;这个问题我们平时在交流群里都有讲过&#xff0c;可是这丫头比较疯&#xff0…

级差制系统开发模式是怎么赚钱的?

级差制是直销所有模式中最受欢迎的模式之一&#xff0c;很多企业商家都会在级差制和双轨制中二选一&#xff0c;可见这个模式的优秀程度。下面就来简单分析一下&#xff0c;在级差制模式中是怎么赚钱的&#xff1f; 级差制最大的特点就是以卖货为主&#xff0c;它所有的奖金设置…

正规理财app软件有哪些?top5资质正规理财app软件最新排名

正规理财app软件有哪些&#xff1f;随着移动端理财的普及&#xff0c;越来越多的人开始使用理财app软件进行投资和资产管理。但是&#xff0c;市场上有很多理财软件&#xff0c;如何选择一款正规、安全的软件是关键。下面就为大家介绍一些选择理财app软件的建议。首先&#xff…

GDT陶瓷气体放电管串电容的5点作用

串电容&#xff0c;是指串联衔接于线路中&#xff0c;其主要目的是用来补偿电力线路感抗的电容器&#xff0c;电容器也是目前电力设备中必不可少的一环&#xff0c;其种类很多。下面优恩将为大家介绍一下GDT陶瓷气体放电管串电容的作用。 据小编了解&#xff0c;GDT陶瓷气体放电…

Bellhop 海底地形起伏条件下的传播特性

文章目录 前言一、预备内容二、水平海底波导&#xff08;水平海底&#xff09;1、海底水平的深海波导中的声线①、环境文件②、Matlab 命令③、执行结果 2、海底水平的深海波导中的本征声线①、环境文件②、Matlab 命令③、执行结果 3、海底水平的深海波导中的相干传播损失①、…

TDEngine3.0 环境安装、配置及使用经验总结

TDEngine3.0 环境安装、配置及使用经验总结 一、TDengine 介绍二、TDengine的下载三、TDengine Server安装及配置3.1 安装3.2 taos的参数配置3.3 启动3.4 taosAdapter 四、TDengine Client 安装4.1 linux客户端安装4.2 windows客户端安装 五、TDEngine3.x的使用总结 一、TDengi…

minhook探究

参考&#xff1a;https://github.com/TsudaKageyu/minhook minhook是windows平台上支持x86/x64的hook库&#xff0c;git上的自我介绍说是“mininalistic",其简约并不简单。在接口的设计&#xff0c;hook的兼容性等方面&#xff0c;还是值得我们初学者解决的。熟悉inline …

Hadoop之HDFS概述

Hadoop概述之HDFS HDFS架构概述优缺点HDFS架构HDFS文件块大小HDFS的shell命令HDFS读写流程写数据流程 HDFS读数据流程NameNode 和 SecondaryNameNode工作机制DataNode工作机制DataNode数据完整性如何保证 端口名称Hadoop2.xHadoop3.xNameNode内部通信端口8020/9000NameNode HTT…

两天搞定计算机专业毕业设计,附源码

两天搞定计算机专业毕业设计&#xff0c;附源码 适用者毕设专业 使用要求具备基本Unity 基本操作小白即可&#xff0c;无需编码 博主诉求快乐毕业 点赞 关注 收藏 资源说明Free资源太多了&#xff0c;看截图目录就知道了 适用者 毕设专业 鄙人也是计算机狗一只&#xff0c;会…

软考A计划-电子商务设计师-复习要点

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

Ampere 又放大招,推出自研192 核AmpereOne 系列处理器,已投产

作者 | 伍杏玲 近日&#xff0c;Ampere Computing 发布2023年度战略和产品路线图&#xff0c;并推出全新的AmpereOne系列处理器&#xff0c;拥有多达 192 个单线程 Ampere 核&#xff0c;内核数量为业界最高。这是第一款基于 Ampere 新自研核的产品&#xff0c;由 Ampere 自有…

java--正则表达式

一、作用 作用一&#xff1a;校验字符串是否满足规则 作用二&#xff1a;在一段文本中查找满足要求的内容 二、符号含义 1、字符类&#xff08;只匹配一个字符&#xff09; 符号含义[abc]只能是a,b或c中一个[^abc]除了a,b,c之外的任何字符[a-zA-Z]a到z A到Z[a-d[m-p]]a到d&…