【排序算法】——选择排序

news2024/12/19 10:26:16

前言

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

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

简介

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

        本篇文章讲述的是排序算法中的选择排序,其中包含了两种排序算法,分别是直接选择排序堆排序,下面将会一一为大家详细介绍。(用升序进行讲解)

      

基本思想

         选择排序算法的基本思想是为每一个位置选择当前最小的元素。

 1.直接选择排序 

        下面我们首先来看一看直接选择排序算法的动图演示:

        看了上图我们可以得知,直接选择排序算法是首先从第1个位置开始对全部元素进行选择,选出全部元素中最小的给该位置,再对第2个位置进行选择,在剩余元素中选择最小的给该位置即可;以此类推,重复进行“最小元素”的选择,直至完成第(n-1)个位置的元素选择,则第n个位置就只剩唯一的最大元素,此时不需再进行选择。 

        直接选择排序算法的思路以及代码都比较简单,有了上述讲解相信大家都已经对其了解了。

 2.堆排序

        直接选择排序是选择排序的一种,但是其时间复杂度很高,在实际应用中效率非常低下,那有没有其他的效率高的选择排序呢?答案当然是有的,那就是堆排序(Heapsort),堆排序主要借助了我们的数据结构--堆来实现。(若是对堆不了解的可以去阅读我的另一篇文章数据结构--堆)。

        当一个堆是大根堆的时候我们知道堆顶元素永远是整个堆中最大的元素,因此每次取堆顶我们都会得到一个最大值(降序则需要用小根堆),这刚好与我们选择排序算法的基本思想相同。下面我将同图画来给大家进行演示:

        此时堆顶元素是数组中最大的元素,将其与最后一个元素交换位置,并对堆进行调整。

       此时对于 9 这个元素我们可以理解为已经把它从该堆中删除了,此时堆中只剩下4个元素,重复此操作即可完成排序,大家可以根据下方的代码具体了解。

代码实现

 1.直接插入排序

        先看原始代码:

void Select_sort(vector<int>& a)
{
    int n = a.size();
    //对于直接选择排序来说,只需要进行n - 1次循环即可
    for (int i = 0; i < n - 1; i++)
    {
        int minpos = i;
        //从i位置开始,遍历其后面的数组,找到最小值
        for (int j = i + 1; j < n; j++)
        {
            if (a[j] < a[minpos])
            {
                minpos = j;
            }
        }
        //将最小值所处的位置与i位置的值进行交换即可
        swap(a[i], a[minpos]);
    }
}

        解析:两次循环即可完成,第一层循环控制需要排序的位置,第二次循环寻找该位置后的最小值。

        对于直接选择排序,我们有一种优化办法,可以使其的时间效率增加一倍,虽说时间复杂度是相同的,杯水车薪,但也是一种思路。 

具体思路:

        第一层循环我们从数组的两端开始遍历;

        第二次循环我们同时寻找其中间的最大值和最小值。

        代码如下:

void Select_sort(vector<int>& a)
{
    int n = a.size();
    //数组大小为奇数,最后会处于left == right;当数组大小为偶数时,最后会处于left > right
    //因此结束条件为left < right
    for (int left = 0, right = n - 1; left < right; left++, right--)
    {
        int minpos = left;
        int maxpos = left;
        //从left位置遍历其后面到right位置之前的数组,找到最小值和最大值
        for (int i = left + 1; i < right; i++)
        {
            if (a[i] < a[minpos])
            {
                minpos = i;
            }
            if (a[i] > a[maxpos])
            {
                maxpos = i;
            }
        }
        //此时在交换元素时需要注意一个细节:
        //当我们将a[right]与a[maxpos]交换时,maxpos位置上之前可能是left的位置,这样在之后的交换会出现问题,因此我们需要进行判断接下来的交换是否还要进行
        swap(a[right], a[maxpos]);
        if (a[minpos] < a[left])
        {
            swap(a[left], a[minpos]);
        }
    }
}

         对于优化后的直接选择排序在最后的交换步骤时的细节需要大家额外注意,大家可以自己用一个倒序数组亲自体验一下,以便有更深刻的体会。

 2.堆排序 

        先看代码:

//向下调整算法
void AdjustDown(vector<int>& a, int parent, int end)
{
    int child = parent * 2 + 1;
    while (child < end)
    {
        if (child + 1 < end && a[child] < a[child + 1])
        {
            child++;
        }

        if (a[child] > a[parent])
        {
            swap(a[child], a[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

void Heap_sort(vector<int>& a)
{
    int n = a.size();
    //首先进行建堆
    for (int i = (n - 1 - 1) / 2; i >= 0; i--)
    {
        AdjustDown(a, i, n);
    }

    //进行排序
    for (int i = n - 1; i > 0; i--)
    {
        swap(a[0], a[i]);
        AdjustDown(a, 0, i);
    }
}

        对于堆排序来说,比较重要的地方是当我们在进行排序时,一定要注意当每一次交换完元素后,堆中的数据就会减少一个,因此当我们在自己写向下调整算法时,一定要注意此时堆中的数据个数,不然就会出现错误。 

        注:对于建堆和向下调整算法不了解的朋友可以先去看一看数据结构--堆,里面有较为详细的介绍。

总结

1.时空复杂度

        经过分析我们可以得到直接选择排序的时间复杂度和空间复杂度,两层for循环以及常数个变量,因此

直接选择排序:

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

        空间复杂度:O(1)

        对于堆排序来说,时间复杂度由建堆操作和排序操作决定,具体的计算过程较为复杂,感兴趣的可以自己搜索一下,这里不再赘述。因此

堆排序:

        时间复杂度:O(NlogN)

        空间复杂度:O(1)

        堆排序算法的总体时间复杂度是 O(n log n),这是因为建堆之后,还需要进行 n-1 次的排序操作,每次排序操作的时间复杂度是 O(log n)。但是,建堆本身的时间复杂度是线性的,这使得堆排序在某些情况下比其他 O(n log n) 排序算法更高效。 

2.稳定性

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

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

        简单分析我们可以知道选择排序算法是不稳定的。举例说明:序列58539.我们知道第一遍选择第1个元素“5”会和元素“3”交换,那么原序列中的两个相同元素“5”之间的前后相对顺序就发生了改变。因此,我们说选择排序不是稳定的排序算法,它在计算过程中会破坏稳定性。(对于直接选择排序以及堆排序都是如此)

直接选择排序: 不稳定

堆排序:            不稳定

尾声

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

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

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

相关文章

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

前言 排序(Sorting) 是计算机程序设计中的一种重要操作&#xff0c;它的功能是将一个数据元素&#xff08;或记录&#xff09;的任意序列&#xff0c;重新排列成一个关键字有序的序列。所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#x…

硬件工程师面试题 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…