【排序算法】选择排序的全面剖析(含详细图解)

news2024/11/26 2:42:22

在之前文章中我们了解到了插入排序👉【插入排序】,现在我们来学习排序算法中的直接选择排序 


目录

💯引言

💯选择排序的原理

💯选择排序的实现步骤

⭐简单选择排序(以升序为例)

⭐代码实现 

 ⭐示例说明

💯选择排序的时间复杂度分析

⭐最好情况时间复杂度

⭐最坏情况时间复杂度

⭐平均情况时间复杂度

💯选择排序的空间复杂度分析

💯选择排序的稳定性分析

💯选择排序与其他排序算法的比较 

⭐与冒泡排序比较

⭐与插入排序比较

⭐与快速排序比较

 💯总结


💯引言

选择排序作为一种简单直观的排序算法,虽然在时间复杂度上并非最优,但对于理解排序的基本原理和算法设计思想具有重要意义。

本文将对选择排序进行深入解析,包括其原理、实现步骤、时间复杂度分析、空间复杂度分析以及与其他排序算法的比较。

💯选择排序的原理

😁选择排序的基本思想:

每一次从待排序的数据元素中选出最小(或最大)的⼀个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

"它的工作方式类似于在一堆无序的卡片中,每次挑选出最小的卡片放在最前面,然后在剩下的卡片中继续挑选,依次类推,最终使所有卡片按照从小到大(或从大到小)的顺序排列。"

😊图解如下: 

💯选择排序的实现步骤

简单选择排序(以升序为例)

 😌首先,遍历整个数组,找到最小的元素。

  • 假设第一个元素是最小的,然后从第二个元素开始逐个与它比较。
  • 如果发现有比当前假设的最小元素更小的元素,就更新最小元素的索引。 

😜当遍历完整个数组后,将找到的最小元素与数组的第一个元素交换位置。

  • 此时,数组的第一个元素就是最小的,已经处于正确的位置。

😛接着,在剩下的未排序元素(从第二个元素开始到最后一个元素)中重复上述步骤。

  • 再次遍历剩余元素,找到其中最小的元素,并与剩余元素中的第一个元素(即数组的第二个元素)交换位置。

😝不断重复这个过程

  • 每次都将剩余未排序元素中的最小元素放置在已排序部分的末尾,直到整个数组都被排序。

 🌷以如下所示的数组为例:

 对于已排序列表中的第一个位置,整个列表按顺序扫描。目前存储 14 的第一个位置,我们搜索整个列表,发现 10 是最低值。

 所以我们用 10 替换 14。经过一次迭代,10(恰好是列表中的最小值)出现在已排序列表的第一个位置。

 

 对于第二个位置,即 33 所在的位置,我们开始以线性方式扫描列表的其余部分。

 我们发现 14 是列表中的第二小值,它应该出现在第二个位置。我们交换这些值。

 经过两次迭代,两个最小的值以排序的方式出现在开头。

😏对数组中的其余项应用相同的过程:

代码实现 

 以下是用 C 语言实现的简单选择排序代码:

void selectionSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        // 交换最小元素和当前位置的元素
        if (minIndex!= i) {
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

 示例说明

假设我们有一个数组[5, 3, 8, 4, 2]

😁下面是选择排序的具体过程:

  • 第一次遍历:
    • 初始假设第一个元素5是最小的,minIndex = 0
    • 从第二个元素3开始比较,发现3 < 5,更新minIndex = 1
    • 继续比较后面的元素842,都比3大。
    • 遍历完后,将3与第一个元素5交换,数组变为[3, 5, 8, 4, 2]
  • 第二次遍历:
    • 现在从第二个元素5开始(因为第一个元素已经排好序),假设5是最小的,minIndex = 1
    • 比较842,发现2最小,更新minIndex = 4
    • 2与第二个元素5交换,数组变为[3, 2, 8, 4, 5]
  • 第三次遍历:
    • 从第三个元素8开始,假设8是最小的,minIndex = 2
    • 比较45,发现4更小,更新minIndex = 3
    • 4与第三个元素8交换,数组变为[3, 2, 4, 8, 5]
  • 第四次遍历:
    • 从第四个元素8开始,假设8是最小的,minIndex = 3
    • 比较558小,更新minIndex = 4
    • 5与第四个元素8交换,数组变为[3, 2, 4, 5, 8],此时数组已完全排序。

💯选择排序的时间复杂度分析

⭐最好情况时间复杂度

在最好情况下,即数组本身就是有序的(升序或降序),选择排序的时间复杂度仍然是O(n^2)。这是因为无论数组是否有序,每次都需要遍历剩余未排序的元素来找到最小(或最大)元素。虽然在最好情况下,每次找到的最小元素总是已经在正确的位置(即第一个未排序元素就是最小的),但仍然需要进行n-1次比较和n-1次交换(假设数组有n个元素)。比较次数的计算公式为:\sum_{i = 0}^{n - 2}(n - i - 1)=\frac{n(n - 1)}{2},交换次数为n-1,忽略常数系数和低阶项,时间复杂度为O(n^2)

⭐最坏情况时间复杂度

在最坏情况下,数组是完全逆序的(升序排序时)或完全正序的(降序排序时)。此时,每次选择最小(或最大)元素都需要遍历整个未排序部分,比较次数和交换次数与最好情况相同,都是O(n^2)。具体来说,比较次数为\frac{n(n - 1)}{2},交换次数为n-1,所以时间复杂度也是O(n^2)

 

⭐平均情况时间复杂度

平均情况下,选择排序也需要大约\frac{n(n - 1)}{2}次比较和n-1次交换。因此,平均时间复杂度同样为O(n^2)


💯选择排序的空间复杂度分析

选择排序是一种原地排序算法,它只需要常数级别的额外空间。在排序过程中,只需要使用一些临时变量来记录最小元素的索引和进行元素交换,不需要额外的数据结构来存储数据。因此,空间复杂度为O(1)


💯选择排序的稳定性分析

😃选择排序是不稳定的排序算法。稳定性是指在排序过程中,如果两个元素相等,它们在排序后的相对顺序是否会保持不变。在选择排序中,当在未排序部分找到最小元素并与已排序部分的元素交换时,如果有多个相等的最小元素,它们的相对顺序可能会发生改变。

例如,有一个数组[5, 3, 5', 4, 2](其中5'表示另一个值为5的元素),在第一次排序时,会将最小的元素2与第一个元素5交换,此时5'的相对位置就发生了变化。原本5'5后面,排序后5'可能会在5的前面,所以选择排序不稳定。


💯选择排序与其他排序算法的比较 

⭐与冒泡排序比较

  1. 时间复杂度
    • 选择排序和冒泡排序的时间复杂度在最坏、最好和平均情况下都是O(n^2)
    • 但是,在实际运行中,选择排序通常会比冒泡排序稍微快一些。这是因为冒泡排序在每一次比较后都可能进行交换操作,而选择排序是在一次遍历完未排序部分找到最小元素后才进行一次交换。
  2. 稳定性
    • 冒泡排序是稳定的排序算法,而选择排序是不稳定的。
  3. 代码实现复杂度
    • 两者的代码实现都相对简单,但是冒泡排序的代码可能更容易理解一些,因为它的交换操作是相邻元素之间进行的,比较直观。而选择排序需要记录最小元素的索引,然后进行交换,逻辑上稍微复杂一点。

⭐与插入排序比较

  1. 时间复杂度
    • 插入排序在最好情况下(数组已经有序)时间复杂度为O(n),平均和最坏情况下时间复杂度为O(n^2)。而选择排序在所有情况下时间复杂度都是O(n^2)
    • 当数组接近有序时,插入排序的性能会比选择排序好很多,因为插入排序在这种情况下可以很快地将新元素插入到已排序部分的合适位置,而选择排序仍然需要进行大量的比较和交换。
  2. 稳定性
    • 插入排序是稳定的排序算法,选择排序不稳定。
  3. 空间复杂度
    • 两者的空间复杂度都是O(1),都是原地排序算法。
  4. 适用场景
    • 如果数据量较小且基本有序,插入排序更合适。如果数据的有序性不确定,且对稳定性没有要求,选择排序可以作为一种简单的选择。

⭐与快速排序比较

  1. 时间复杂度
    • 快速排序的平均时间复杂度为O(nlogn),在最坏情况下时间复杂度会退化为O(n^2)(当分区极度不平衡时)。选择排序始终是O(n^2)
    • 因此,在大多数情况下,快速排序比选择排序效率高得多。
  2. 稳定性
    • 快速排序也是不稳定的排序算法,和选择排序一样。
  3. 空间复杂度
    • 快速排序的空间复杂度在平均情况下为O(logn)(由于递归调用需要栈空间),最坏情况下为O(n)。选择排序的空间复杂度为O(1)
  4. 适用场景
    • 对于大规模数据的排序,快速排序通常是更好的选择,因为它的平均性能更好。但如果对稳定性有要求或者数据量较小,并且不希望使用递归(因为递归可能会导致栈溢出等问题),选择排序可能更合适。

 💯总结

 选择排序是一种简单易懂的排序算法,其原理直观,代码实现相对容易。它具有原地排序和时间复杂度始终为O(n^2)的特点,适用于数据量较小且对算法效率要求不高的场景。

在理解排序算法的基本思想和进行简单数据排序时,选择排序是一个很好的学习和实践案例。


💝💝💝感谢你看到最后,点个赞再走吧!💝💝💝我的主页👉【A Charmer】

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

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

相关文章

Human-M3 多模态姿态估计数据集-初步解读

文章概述(个人总结):该论文重点提出一个用于人体姿态估计的RGB+点云数据集,针对该多模态数据集,作者阐述了数据集的收集、数据标注以及该数据集的特点。并提出了一个简单的多模态3D人体姿态估计算法,对比其他模型,该方法性能较好。最后总结了该数据集和该方法的限制。 …

沪尚茗居装修秘籍:嵌入式蒸烤箱,让厨房生活更精彩

在装修厨房时&#xff0c;选择一款合适的嵌入式蒸烤箱不仅能提升烹饪效率&#xff0c;还能为厨房增添一份现代感。沪尚茗居深知用户对厨房电器的需求&#xff0c;从实际出发&#xff0c;为用户推荐选购嵌入式蒸烤箱的实用技巧&#xff0c;让厨房生活更加美好。    首先&…

【千库网-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

leetcode二叉树(二)-二叉树的递归遍历

题目 . - 力扣&#xff08;LeetCode&#xff09; . - 力扣&#xff08;LeetCode&#xff09; . - 力扣&#xff08;LeetCode&#xff09; 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出…

滑块验证码,给图就行

效果如上~ <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>滑块拼图验证码 - 拖动条 Loading 效…

K8s环境下使用sidecar模式对EMQX的exhook.proto 进行流量代理

背景 在使用emqx作为mqtt时需要我们需要拦截client的各种行为&#xff0c;如连接&#xff0c;发送消息&#xff0c;认证等。除了使用emqx自带的插件机制。我们也可以用多语言-钩子扩展来实现这个功能&#xff0c;但是目前emqx仅仅支持单个grpc服务端的设置&#xff0c;所以会有…

视频格式转换

格式转换 1️⃣Convertio &#x1f3c6;优点 速度快、格式多、免费免登录 缺点 超过100m的文件就要登录付费了 点击进入 2️⃣123APPs &#x1f3c6;优点 这个免费的文件上限是10G&#xff0c;完完全全够用功能多、除了转换器格式还有视频、音频、PDF处理工具 缺点 广…

无人机飞手执照培训,三类、四类傻傻分不清楚

无人机飞手执照培训中的三类和四类&#xff0c;主要依据无人机的空机重量进行区分&#xff0c;并对应不同的飞行权限和应用场景。以下是对这两类执照的详细解析&#xff1a; 一、无人机飞手执照的三类与四类定义 1. 三类执照&#xff1a; 定义&#xff1a;三类执照是指允许操…

架构设计笔记-9-软件可靠性

目录 知识要点 综合知识 案例分析 1.可靠性特性&#xff0c;软硬件可靠性对比 论文 1.论软件可靠性设计技术的应用 知识要点 软件架构需求过程主要是获取用户需求&#xff0c;标识系统中所要用到的构件&#xff0c;并进行架构需求评审。其中&#xff0c;标识构件又详细地…

Redis——持久化

文章目录 Redis持久化Redis的两种持久化的策略定期备份&#xff1a;RDB触发机制rdb的触发时机&#xff1a;手动执行save&bgsave保存测试不手动执行bgsave测试bgsave操作流程测试通过配置&#xff0c;自动生成rdb快照RDB的优缺点 实时备份&#xff1a;AOFAOF是否会影响到red…

去耦电容的“滤波半径”

1、简介 去耦电容的滤波半径通常指的是在电路板上&#xff0c;去耦电容能够对其周围电源线路或信号线路产生有效去耦作用的范围。这个范围是以去耦电容为中心&#xff0c;向周围扩展的一个特定距离。 想象你有一个水桶&#xff0c;里面装满了混浊的水&#xff08;含有噪声的信…

基于ESP32的厨房计时器

基于ESP32的厨房计时器 一、项目说明二、项目材料三、OLED显示屏四、外壳设计五、外壳打印六、电路和外壳的集成七、编程八、成品展示 一、项目说明 厨房计时器很有用&#xff0c;但现在没有多少人使用实体厨房计时器了。我个人还是喜欢使用它们&#xff0c;因为拥有一个可以按…

CGAL 带约束的Delaunay三角剖分

CGAL 带约束的Delaunay三角剖分 本文使用CGAL进行简单的2D Delaunay 三角剖分,添加内外边界及点作为约束剖分。 Code #include <CGAL/Exact_predicates_inexact_constructions_kernel.h> #include <CGAL/Constrained_Delaunay_triangulation_2.h> #include <…

问题杂录-NVIDIA Bluefield DPU bfb-build编译报错记录与处理办法?(无数坑)

文章目录 背景bfb-build之后直接退出docker.io无法访问报错 ERROR: failed to solve: processkubernotes 下载失败报错mlnx-fw-updater-signed-24.07-0.6.1.1.aarch64: Cannot download报错 No match for argument: bf-release报错 放弃编译anolis&#xff0c;直接编译老版ubun…

RabbitMQ 入门(二)基本结构和消息模型

一、RabbitMQ的基本结构、角色和消息模型 MQ的基本结构&#xff1a; RabbitMQ中的一些角色&#xff1a; - publisher&#xff1a;生产者 - consumer&#xff1a;消费者 - exchange个&#xff1a;交换机&#xff0c;负责消息路由 - queue&#xff1a;队列&#xff0c;存储消息…

初步认识torch自定义算子

此篇为PyTorch 自定义算子&#xff1a;复现CPU和CUDA版的二维卷积的代码详解 这篇是为了展示setup在构建简单的cpp算子的使用 1.环境配置 整体结构如下图所示 pytorch_cpp_helper.hpp中准备了CPU版卷积需要的头文件 pytorch_cuda_helper.hpp和common_cuda_helper.hpp是cuda…

板级支持包构建1

开发板&#xff1a;STM32h743xi 编程软件&#xff1a;Keil 项目&#xff1a;GPIO外设操作&#xff08;彩色LED灯&#xff09; 学习打卡&#xff1a;Day2 学习地址&#xff1a;【野火】STM32 HAL库开发实战指南 教学视频 手把手教学STM32全系列 零基础入门CubeMXHAL库&#xff0…

jQuery——自定义jQuery插件

1、扩展jQuery&#xff08;将$看成对象&#xff09;的工具方法 $.extend&#xff08;object&#xff09; min&#xff08;a&#xff0c;b&#xff09; 返回较小的值 max&#xff08;c&#xff0c;d&#xff09; 返回较大的值 leftTrim&#xff08;&#xff09; 去掉字符串…

9.4 栅格图层符号化山体阴影渲染

9.4 栅格图层符号化山体阴影渲染-CSDN博客 目录 前言 山体阴影渲染 QGis设置为山体阴影 二次开发代码实现山体阴影 总结 前言 介绍栅格图层数据渲染之山体阴影渲染说明&#xff1a;文章中的示例代码均来自开源项目qgis_cpp_api_apps 山体阴影渲染 以“3420C_2010_327_…

leetcode二叉树(一)-理论基础

本节主要参考代码随想录&#xff1a;代码随想录 题目分类 二叉树的种类 满二叉树 满二叉树&#xff1a;如果一棵二叉树只有度为0的结点和度为2的结点&#xff0c;并且度为0的结点在同一层上&#xff0c;则这棵二叉树为满二叉树。 这棵二叉树为满二叉树&#xff0c;也可以说深…