【排序算法】——交换排序

news2024/12/19 10:24:13

 

前言

        排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列。所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作

        排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。

简介

        所谓排序算法,即通过特定的算法因式将一组或多组数据按照既定模式进行重新排序。这种新序列遵循着一定的规则,体现出一定的规律,因此,经处理后的数据便于筛选和计算,大大提高了计算效率。对于一个排序算法的优劣,我们需要从它的时间复杂度、空间复杂度和稳定性三个方面来考虑。什么叫稳定性呢?即当两个相同的元素同时出现于某个序列之中,则经过一定的排序算法之后,两者在排序前后的相对位置不发生变化。换言之,即便是两个完全相同的元素,它们在排序过程中也是各有区别的。

        本篇文章讲述的是排序算法中的交换排序,其中包含了两种排序算法,分别是冒泡排序快速排序,下面将会一一为大家详细介绍。(用升序进行讲解)

基本思想

        所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置。交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

1.冒泡排序

        冒泡排序的基本思想是把较小的元素往前调或者把较大的元素往后调。这种方法主要是通过对相邻两个元素进行大小的比较,根据比较结果和对该两元素的位置进行交换,这样逐个依次进行比较和交换,就能达到排序目的。        

        冒泡排序是一种最基础的交换排序。之所以叫做冒泡排序,是因为每⼀个元素都可以像小气泡⼀样,根据自身大小一点一点向数组的一侧移动。冒泡排序基本上可以说是我们计算机人所接触的第一个排序算法了。我们先来通过一个动图来体会一下冒泡排序:

        第一轮,我们对第1个元素进行排序,先将它和第2个元素比较大小,如果是大于第二个元素,就将这两个元素进行交换,再对第2个和第3个元素进行比较,依次类推,重复进行上述计算,直至完成第(n一1)个和第n个元素之间的比较,此后,再按照上述过程进行第2次、第3次排序,直至整个序列有序为止。相信大家已经对冒泡排序有了深刻的认识了。

2.快速排序 

        快速排序也就是我们平常所说的快排,是Hoare于1962年提出的一种二叉树结构的交换排序方法,快速排序的基本思想是 : 任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后对左右子序列重复该过程,直到所有元素都排列在相应位置上为止,最终能够实现将所需排序的无序序列元素变为一个有序的序列。

        快速排序有有很多版本,大致有 hoare版本,挖坑法,lomuto版本等等,在这三种中前两种都是用左右指针来实现快排的过程,而第三种则是使用前后指针来实现。在这里只为大家讲解lomuto版本,若是对其他方法感兴趣的可以去搜索了解一下。

 lomuto版本 :

        lomuto版本的思路是创建前后指针,从左往右找比基准值小的进行交换,使得小的都排在基准值的左边。

        我们先创建两个指针 prevcur ,具体过程大家跟着动图可以深刻理解:

         快速排序的时间效率与我们所选的 Key 值有很大的关系,若是我们每次所选的 Key 值都有许多与其重复的元素,那么快速排序的时间效率就会急速下降。例如,当我们用快速排序对一个所有元素都相同的数组进行排序时,因为每一趟都找不到比 Key 小的值,所以每一趟都是 Keyprev 原地交换,这样下来,时间复杂度就来到了O(N ^ 2),那有没有什么方法能够解决这种方法呢?当然是有的,那就是使用三路划分。

三路划分 :   

        什么是三路划分呢?顾名思义,就是把一个数组分为三个部分,我们上面讲解的 lomuto版本以及其他版本都没有考虑与 Key 值相同的元素该放在哪里,既可以放在左边,也可以放在右边。那么我们的三路划分就是把与 Key 值相同的元素放在中间,我们可以把三路划分看作lumotoHoare 两种方法的结合。可以通过下图具体了解:

         这便是进行一趟排序后的结果,整个数组分为三个部分,left 左边的都小于 Key 值,right 的右边都大于 Key 值,而 leftright 中间的值即为与 Key 相等的值。这便是三路划分,具体的方法如下:

首先我们需要两个三个指针 left、 right、 cur。

        1. Key 默认取 left 的值。

        2. left 指向区间最左边,right 指向区间最右边,cur 指向 left + 1 位置。

        3. 开始循环比较

        4. cur 遇到比 Key 小的值后跟 left 位置交换,换到左边,然后 left++,cur++。

        5. cur 遇到比 Key 大的值后跟 right 位置交换,换到右边,然后 right--。

        6. cur 遇到跟 Key 相等的值后, cur++。

        7. cur > right 时循环结束。

        这就是三路划分的具体思路。在解决重复元素较多的排序时三路划分的效率非常高,但有得必有失,当解决重复元素很少的排序时三路划分的时间效率远不及lomuto等版本的时间效率。因此,我们在不同场景下,要学会选择合适的方法。

代码实现

1. 冒泡排序 :

         冒泡排序是最基础的排序算法,不仅易理解而且代码也非常简洁,下面是代码展示:

void Bubble_Sort(vector<int>& a)
{
    int n = a.size();
    //第一层for循环控制循环的趟数
    for (int i = 0; i < n - 1; i++)
    {
        //定义一个标志,一趟中没有发生交换时,说明数组已经有序,后面不必进行,直接结束即可
        int flag = 0;
        //第二层for循环进行比较
        for (int j = 0; j < n - i - 1; j++)
        {
            if (a[j] > a[j + 1])
            {
                swap(a[j], a[j + 1]);
                flag = 1;
            }
        }
        if (flag == 0)
        {
            break;
        }
    }
}

2. 快速排序 :

        对于快速排序来说,我们既可以用递归的方式去实现它的代码,当然也可以用非递归的方式,用非递归的方式来实现快速排序时需要借助数据结构--栈来实现,感兴趣的可以自己去试一试,这里只用递归的方式为大家展示。 

 lomuto

        首先是适用于重复元素较少的lomuto版本,代码如下:

void Quick_Sort(vector<int>& a, int left, int right)
{
    //函数出口 : 当left >= right时,结束递归
    if (left >= right)
    {
        return;
    }

    //用随机数来选取key值
    int randi = left + (rand() % (left - right));
    swap(a[left], a[randi]);
    
    int prev = left, cur = prev + 1, keyi = left;
    while (cur <= right)
    {
        //当a[cur] < a[keyi]并且prev加一后不等于cur时,交换两者的位置,这是因为当prev加一后如果等于cur,就相当于自己和自己交换
        if (a[cur] < a[keyi] && ++prev != cur)
        {
            swap(a[cur], a[prev]);
        }
        cur++;
    }
    //循环结束后,prev位置上的值一定是小于key值的,因此将key值也就是keyi位置上的值与prev位置上的值进行交换
    swap(a[keyi], a[prev]);

    //对左边以及右边继续进行快排
    Quick_Sort(a, left, prev - 1);
    Quick_Sort(a, prev + 1, right);
}

 三路划分:

        接下来是适用于重复元素较多时的三路划分方法,代码如下:

void Quick_Sort(vector<int>& a, int l, int r)
{
    //函数出口 : 当left >= right时,结束递归
    if (l >= r)
    {
        return;
    }
    
    int left = l, right = r;
    int cur = left + 1, key = a[left];
    while (cur <= r)
    {
        if (a[cur] < key)
        {
            swap(a[left], a[cur]);
            left++;
            cur++;
        }
        else if (a[cur] < key)
        {
            swap(a[cur], a[right]);
            right--;
        }
        else
        {
            cur++;
        }
    }

    //对左边以及右边继续进行快排
    Quick_Sort(a, l, left - 1);
    Quick_Sort(a, right + 1, r);
}

        若是对中间循环部分不理解可以自己画一个图,再结合代码相信能够很好的理解。 

总结

1.时空复杂度

        对于冒泡排序,两层for循环,不用说的O(N ^ 2)以及常数级的变量,因此

冒泡排序:

        时间复杂度:O(N ^ 2)

        空间复杂度:O(1)

        对于快速排序,因为是基于二叉树所创造的排序方法,因此其时间复杂度为O(NlogN),至于具体的计算方法感兴趣的可以百度搜索一下。因为我们递归需要开辟函数栈帧,所以空间上会有所消耗,因此

 快速排序:

        时间复杂度:O(NlogN)

        空间复杂度:O(logN ~ N)

2.稳定性

        在排序算法中,我们不光要关注算法的时空复杂度,还在看看算法的稳定性,什么是稳定性呢?

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

        冒泡排序过程中要特别注意的是,当相邻两个元素大小一致时,这一步操作就不需要交换位置,因此也说明冒泡排序是一种严格的稳定排序算法,它不改变序列中相同元素之间的相对位置关系。  而对于快速排序来说,元素并不能保证相同元素的相对位置,因此快速排序是不稳定。

冒泡排序 稳定

快速排序 不稳定

尾声

        若有纰漏或不足之处欢迎大家在评论区留言或者私信,同时也欢迎各位一起探讨学习。感谢您的观看!

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

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

相关文章

硬件工程师面试题 11-20

把常见的硬件面试题进行总结&#xff0c;方便及时巩固复习。其中包括网络上的资源、大佬们的大厂面试题&#xff0c;其中可能会题目类似&#xff0c;加强印象即可。 11、示波器需要关注哪些参数&#xff1f; 1&#xff0c;示波器带宽 带宽是示波器最重要的指标之一。 模拟示波器…

【Yolov8足球追踪】YOLO格式足球检测数据集制作及目标追踪

可以只是已经制作好的数据集&#xff0c;也可以进行制作&#xff1a; 数据集制作&#xff1a;1&#xff0c;2&#xff0c;3步 数据集下载&#xff1a; https://pan.baidu.com/s/1upT_aD06lzjuz-Xjze0dcw 提取码: 6eax windows下分卷解压文件&#xff1a;https://blog.csdn.net/…

4.9 TCP 拥塞控制算法

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 1 TCP 拥塞控制算法1.1 慢开始&#xff08;Slow Start&#xff09;1.2 拥塞避免&#xff08;Congestion Avoidance&#xff09;1.3 快重传&#xff08;Fast Retransmit&#x…

计算机毕业设计Django+Tensorflow音乐推荐系统 音乐可视化 卷积神经网络CNN LSTM音乐情感分析 机器学习 深度学习 Flask

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

【HarmonyOS】HarmonyOS 和 Flutter混合开发 (一)之鸿蒙Flutter环境安装

【HarmonyOS】HarmonyOS 和 Flutter混合开发 &#xff08;一&#xff09;之鸿蒙Flutter环境安装 一、前言 flutter作为开源适配框架方案&#xff0c;已经在Android&#xff0c;IOS&#xff0c;Web&#xff0c;Window四大平台进行了适配&#xff0c;一套代码&#xff0c;可以同…

机器人变换关系

rigid 刚性变换&#xff08;平移、旋转&#xff09;&#xff1a; 只改变位置和方向&#xff0c;拥有6个自由度&#xff0c;保持变换物体的形状。 affine 仿射变换&#xff08;平移、旋转、缩放、倾斜等&#xff09;&#xff1a; 改变物体形状、位置、方向&#xff0c;拥有12…

RTU 通信模块赋能智慧路灯远程开关管理,点亮智慧城市节能增效

RTU&#xff08;Remote Terminal Unit&#xff09;远端测控单元在智慧路灯远程开关管理系统中主要负责数据通信和开关控制。能够实现对路灯设备的远程监测和控制&#xff0c;将路灯的状态信息&#xff08;如开关状态、故障信息、亮度参数等&#xff09;上传到管理平台&#xff…

Macbookpro M1 IDEA中安装mysql

一&#xff1a;安装与连接数据库 1. 首先在mysql中创建一个初始数据库&#xff1a;idea_db&#xff0c;如示&#xff1a; 2.打开IDEA,如果最右侧没有database窗口&#xff0c;则在插件那里下载“Database navigator”,稍后重启一下即可&#xff1b; 点击最右侧Database---->…

ISCTF复现-misc

File_Format 下载附件后用010打开查看文件头会发现是个exe文件 格式&#xff1a;文件描述&#xff08;后缀名&#xff09;&#xff0c;文件头(hex)&#xff1a;文件头标识&#xff08;十六进制&#xff09;PNG (png)&#xff0c;文件头(hex)&#xff1a;89504E47 PNGImageFile…

如何有效的开展接口自动化测试?

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、简介 接口自动化测试是指使用自动化测试工具和脚本对软件系统中的接口进行测试的过程。其目的是在软件开发过程中&#xff0c;通过对接口的自动化测试来提高测…

FFmpeg第一话:FFmpeg 简介与环境搭建

FFmpeg 探索之旅 一、FFmpeg 简介与环境搭建 二、FFmpeg 解码详解 第一话&#xff1a;FFmpeg 简介与环境搭建 FFmpeg 探索之旅一、前言二、FFmpeg 是什么&#xff1f;三、简单介绍其历史背景四、为什么用 C学习 FFmpeg&#xff1f;&#xff08;一&#xff09;高性能优势&#…

git branch -r(--remotes )显示你本地仓库知道的所有 远程分支 的列表

好的&#xff0c;git branch -r 这个命令用于列出远程分支。让我详细解释一下&#xff1a; 命令&#xff1a; git branch -rdgqdgqdeMac-mini ProductAuthentication % git branch -rorigin/main作用&#xff1a; 这个命令会显示你本地仓库知道的所有 远程分支 的列表。它不…

解锁 draw.io 流程图制作工具的强大功能与应用(1/2)

一、draw.io 简介 &#xff08;一&#xff09;基本概述 draw.io 是一款由 JGraph 公司开发的基于网页的在线图表绘制工具。它最大的优势之一就是无需进行繁琐的下载和安装步骤&#xff0c;只要打开浏览器&#xff0c;访问其官网&#xff0c;就能立即开始使用。无论是在 Window…

数据结构与算法:稀疏数组

前言 此文以整型元素的二维数组为例&#xff0c;阐述稀疏数组的思想。其他类型或许有更适合压缩算法或者其他结构的稀疏数组&#xff0c;此文暂不扩展。 稀疏数组的定义 在一个二维数据数组里&#xff0c;由于大量的元素的值为同一个值&#xff0c;比如 0或者其他已知的默认值…

【物联网技术与应用】实验4:继电器实验

实验4 继电器实验 【实验介绍】 继电器是一种用于响应施加的输入信号而在两个或多个点或设备之间提供连接的设备。换句话说&#xff0c;继电器提供了控制器和设备之间的隔离&#xff0c;因为设备可以在AC和DC上工作。但是&#xff0c;他们从微控制器接收信号&#xff0c;因此…

RV1126平台下的MobileSeg量化指南:高效部署低算力硬件

1 引言 MobileSeg系列模型采用编解码架构&#xff0c;并以轻量级模型作为骨干网络&#xff0c;专为低算力硬件&#xff08;如BPU、NPU、ARM CPU等&#xff09;设计。这使得MobileSeg在边缘设备和移动端应用中表现出色&#xff0c;具备高效能和灵活性。本文将以MobileSeg分割模…

随手记:小程序兼容后台的wangEditor富文本配置链接

场景&#xff1a; 在后台配置wangEditor富文本&#xff0c;可以文字配置链接&#xff0c;图片配置链接&#xff0c;产生的json格式为&#xff1a; 例子&#xff1a; <h1><a href"https://uniapp.dcloud.net.cn/" target"_blank"><span sty…

RabbitMQ的核心组件有哪些?

大家好&#xff0c;我是锋哥。今天分享关于【RabbitMQ的核心组件有哪些&#xff1f;】面试题。希望对大家有帮助&#xff1b; RabbitMQ的核心组件有哪些&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 RabbitMQ是一个开源的消息代理&#xff08;Messag…

html基础-认识html

1.什么是html html是浏览器可以识别的的标记语言&#xff0c;我们在浏览器浏览的网页就是一个个的html文档 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>认识html</title> </head> <body><h1…

电子应用设计方案-59:智能电动床系统方案设计

智能电动床系统方案设计 一、引言 智能电动床作为一种高端家居产品&#xff0c;旨在为用户提供更加舒适、便捷和个性化的睡眠体验。本方案将详细介绍智能电动床系统的设计架构、功能特点和技术实现。 二、系统概述 1. 系统目标 - 实现床体的多部位电动调节&#xff0c;满足不…