排序算法指南:选择排序

排序算法指南:选择排序

前言:

       选择排序(Selection Sort)是一种基础的排序算法,其核心思路是:在每一轮遍历中,从剩余未排序元素中选出最小(或最大)值,并将其放置在已排序序列的末端。

       对于排序算法的实现,由局部到整体的思路,先排序好一趟或一个元素,再排列多趟或全部元素。

     

        

一、选择排序的工作原理

        

以排序升序数组为例,工作原理如下:

初始化:假设当前数组中,前部分是已经排好序的,后部分是未排序的。

        

寻找最小(或最大)值:遍历未排序的部分,找出其中的最小值(或最大值)。

        

交换位置:将找到的最小值与当前未排序部分的第一个元素交换。

        

重复:缩小未排序部分的范围,重复以上步骤,直到整个数组排好序。

        

如下动图所示:

        

        

                

以上述数组为例,假设有一个待排列的数组为:[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]。

第一轮排序:

        

        当前未排序部分:[3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]

        

        最小的值是 2。


        

        将 2 和未排序部分的第一个元素 3 交换,数组变为:[2, 44, 38, 5, 47, 15, 36, 26, 27, 3, 46, 4, 19, 50, 48]

        
第二轮排序:

        

        当前未排序部分:[44, 38, 5, 47, 15, 36, 26, 27, 3, 46, 4, 19, 50, 48]

        

        最小值是 3。

        

        将 3 和未排序部分的第一个元素 44 交换,数组变为:[2, 3, 38, 5, 47, 15, 36, 26, 27, 44, 46, 4, 19, 50, 48]

                
第三轮排序:

        

        当前未排序部分:[38, 5, 47, 15, 36, 26, 27, 44, 46, 4, 19, 50, 48]


        

        最小值是 4。

        

        将 4 和未排序部分的第一个元素 38 交换,数组变为:[2, 3, 4, 5, 47, 15, 36, 26, 27, 44, 46, 38, 19, 50, 48]

        
第四轮排序:



        当前未排序部分:[5, 47, 15, 36, 26, 27, 44, 46, 38, 19, 50, 48]

        

        最小值是 5(它已经排好序,所以不需要交换)。

        

        数组仍然是:[2, 3, 4, 5, 47, 15, 36, 26, 27, 44, 46, 38, 19, 50, 48]

        
第五轮排序:

        

        当前未排序部分:[47, 15, 36, 26, 27, 44, 46, 38, 19, 50, 48]



        最小值是 15。


        

        将 15 和未排序部分的第一个元素 47 交换,数组变为:[2, 3, 4, 5, 15, 47, 36, 26, 27, 44, 46, 38, 19, 50, 48]

        
第六轮排序:

        

        当前未排序部分:[47, 36, 26, 27, 44, 46, 38, 19, 50, 48]


        

        最小值是 19。

        

        将 19 和第一个元素 47 交换,数组变为:[2, 3, 4, 5, 15, 19, 36, 26, 27, 44, 46, 38, 47, 50, 48]

        
第七轮排序:

        

        当前未排序部分:[36, 26, 27, 44, 46, 38, 47, 50, 48]


        

        最小值是 26。

        

        将 26 和第一个元素 36 交换,数组变为:[2, 3, 4, 5, 15, 19, 26, 36, 27, 44, 46, 38, 47, 50, 48]

        
第八轮排序:



        当前未排序部分:[36, 27, 44, 46, 38, 47, 50, 48]

        

        最小值是 27。


        

        将 27 和第一个元素 36 交换,数组变为:[2, 3, 4, 5, 15, 19, 26, 27, 36, 44, 46, 38, 47, 50, 48]

        
第九轮排序:

        

        当前未排序部分:[36, 44, 46, 38, 47, 50, 48]

        

        最小值是 36(它已经排好序,所以不需要交换)。

        

        数组仍然是:[2, 3, 4, 5, 15, 19, 26, 27, 36, 44, 46, 38, 47, 50, 48]

        
第十轮排序:

        

        当前未排序部分:[44, 46, 38, 47, 50, 48]

        

        最小值是 38。


        

        将 38 和第一个元素 44 交换,数组变为:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 46, 44, 47, 50, 48]

        
第十一轮排序:


        

        当前未排序部分:[46, 44, 47, 50, 48]

        

        最小值是 44。

        

        将 44 和第一个元素 46 交换,数组变为:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 50, 48]

        
第十二轮排序:

        

        当前未排序部分:[46, 47, 50, 48]



        最小值是 46(它已经排好序,所以不需要交换)。

        

        数组仍然是:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 50, 48]

        
第十三轮排序:

        

        当前未排序部分:[47, 50, 48]

        

        最小值是 47(它已经排好序,所以不需要交换)。


        

        数组仍然是:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 50, 48]

        
第十四轮排序:

        

        当前未排序部分:[50, 48]


        

        最小值是 48。

        

        将 48 和第一个元素 50 交换,数组变为:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

        
第十五轮排序:



        当前未排序部分:[50]



        只有一个元素,已经排好序,排序完成。

        

        最终排序结果:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

        

二、选择排序的代码实现

        

void SelectSort(int* a, int n) { //i控制的逻辑:需要n-1趟排序才能使得待排数组有序 for (int i = 0; i < n; i++) { //默认最小值下标为未排序元素中的第一个 int min_index = i; //j控制的逻辑:一趟排序中,寻找未排序元素的最小值所在的位置 //不与自己比较,从i+1开始寻找 for (int j = i+1; j < n ; j++) { if (a[j] < a[min_index]) { //更新下标 min_index = j; } } //交换 Swap(&a[i], &a[min_index]); } }

        

        

三、选择排序的优化

       

        当前版本的选择排序算法在每轮遍历中仅能确定一个最小值(或最大值)。我们考虑对其进行优化,使每轮排序能同时确定最大值和最小值,将最小值置于数组前端,最大值置于末尾,通过这种双端选择的方式,可显著提升排序效率。

        

void SelectSort(int* a, int n) { //定义两个位置, //begin:存放一趟排序中选出的最小值 int begin = 0; //end:存放一趟排序中选出的最大值 int end = n - 1; while (begin < end) //当begin>=end时,待排数组已经有序 { //定义两个下标 //mini:指向未排序元素中的最小值的下标 //maxi:指向未排序元素中的最大值的下标 //两者默认都初始化为begin int mini=begin; int maxi=begin; //i控制的逻辑:遍历未排序的数组元素 for (int i = begin+1; i <= end; i++) { if (a[i] < a[mini]) mini = i; if (a[i] > a[maxi]) maxi= i; } //找到了最小值元素的下标,进行交换 Swap(&a[begin], &a[mini]); //特别注意,若maxi在begin位置上 //将a[begin]与a[mini]交换后,此时最大值的元素被换在了mini的位置 //所以此时maxi的下标要更新未mini if (maxi == begin) { maxi = mini; } //找到了最大值元素的下标,进行交换 Swap(&a[end], &a[maxi]); begin++; end--; } }

一般情况:

        

        

特殊情况:

        

四、选择排序的时空复杂度

        

4.1 时间复杂度 

        

无论最好、最坏还是平均情况,选择排序的时间复杂度都是:O(n^2)

        

简单推导如下:

        

第 1 趟:从 n 个元素里找最小值,要比较 n−1次

        

第 2 趟:从剩下的 n-1 个元素里找最小值,要比较 n−2次

        



        

第 n-1 趟:比较 1 次

        

比较总次数为:(n−1) + (n−2) + ⋯ + 2 + 1 =  ( n - 1 ) * n / 2 

        

故而时间复杂度为O(n^2)

        

4.2 空间复杂度 

        

选择排序是原地排序,只需要常数个辅助变量(如保存当前最小值下标等),不需要额外开辟与 n 成比例的空间。

        

 因此空间复杂度为:O(1)

        

        

                

        

既然看到这里了,不妨关注+点赞+收藏,感谢大家,若有问题请指正。

Read more

深度解析之算法之分治(快排)

深度解析之算法之分治(快排)

44.颜色分类 题目链接 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sort 函数的情况下解决这个问题。 示例 1: 输入: nums = [2,0,2,1,1,0] 输出:[0,0,1,1,2,2] 示例 2: 输入: nums = [2,0,1] 输出:

By Ne0inhk
时间序列数据挖掘 10 大算法全解析 + 3 大核心算法实战

时间序列数据挖掘 10 大算法全解析 + 3 大核心算法实战

时间序列数据贯穿金融、气象、电商、医疗、工业等千行百业,从股价预测、商品销量预估到灾害预警、设备故障预判,其挖掘价值无处不在。但时序数据自带的趋势性、季节性、长依赖、非平稳性等特性,往往让算法选型、数据预处理与落地实施成为数据从业者的核心痛点。本文系统梳理了 10 大主流时间序列数据挖掘算法,从核心原理、适用场景、优势局限三大维度层层拆解,通过多维度对比表明确选型逻辑;总结了 “预处理 - 选型 - 调优” 全流程最佳实践,规避常见踩坑点;并聚焦工业界最常用的 ARIMA、LSTM、Prophet 三大算法,基于真实月度气温数据集(2000-2019 年)提供可直接复用的实战案例、完整代码及可视化结果分析,帮助数据科学家、分析师快速突破时序挖掘瓶颈,实现从 “理论认知” 到 “业务落地” 的无缝衔接。 一、时间序列数据:定义与核心特征

By Ne0inhk
数据结构—顺序表

数据结构—顺序表

数据结构—顺序表 * 线性表 * 顺序表 * 概念与结构 * 顺序表和数组区别 * 分类 * 静态顺序表 * 动态顺序表 * 动态顺序表模拟实现 * 定义动态顺序表结构 * 顺序表初始化 * 顺序表销毁 * 顺序表打印 * 顺序表动态扩容 * 尾插 * 头插 * 尾删 * 头删 * 查找 * 指定位置之前插入 * 删除pos位置的数据 * 竞赛中的静态顺序表 * 静态申请数组 * 封装静态顺序表 * 动态顺序表--vector * 创建vector * size / empty * begin / end * push_back / pop_back * front / back * resize * clear * insert / erase * 仓库—代码总结 线性表 线性表(linear list)是

By Ne0inhk
【数据结构指南】高频二叉树节点问题

【数据结构指南】高频二叉树节点问题

前言:               在熟练掌握二叉树四种基本遍历方法的基础上,本文将深入探讨以下进阶问题:节点总数统计、叶子节点计算、第k层节点数量确定、节点的查找以及树高测量。         这些内容将帮助读者深化对二叉树结构的理解与应用能力,以及深入理解递归分治思想。            一、前置说明:          本文所描述的二叉树都是链式二叉树,其定义方式如下所示:          typedef char BTDataType; typedef struct BinaryTree { BTDataType data; struct BinaryTree* left; struct BinaryTree* right; }BTNode;          二、二叉树的创建及销毁          通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树,其中'#'表示该节点为NULL,二叉树如下图所示:                   前序遍历的思想为: 先访问根节点  ->  再访问左子树 -&

By Ne0inhk