排序算法见解(2)

1.快速排序

1.1基本思想:

快速排序是通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

1.2基本思路:

(1)选择基准:从待排序的序列中选取一个元素作为基准,这个元素可以是序列的第一个、最后一个、中间的一个或者通过某种方式计算得到的值等。

(2)分区:重新排列序列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于序列的中间位置。这个称为分区操作。

(3)递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。

1.3图解过程:

1.3.1hoare版本:

(左边找大,右边找小(先从右边开始找),找到后交换两个位置的值,直到 left=right ,再把 tmp 位置的值与 left 位置的值交换)

动图展示:

这里 tmp 值从右边选取,所以从左先开始找,效果其实一样的。

1.3.2挖坑法:

(1)选 left 做 tmp(tmp 是单趟排序后能排到最后该待的位置的数据,left 同时也作为第一个坑)
(2)right 开始找,right 遇到比 tmp 大或相等的就往左边走,遇到比 tmp 小的就停下,然后将right 赋值给 left(left = right)(填 left 坑),right 则成为了新的坑,进行第三步。(若 right 超过了 left,直接进行第四步)
(3)left 开始找,left 遇到比 tmp 小或相等的就往右边走,遇到比 tmp 大的就停下,然后将 left 赋值给 right(right = left)(填 right 坑),left 则成为了新的坑,进行第二步。(若 left 超过了right,直接进行第四步)
(4)将 tmp 赋值给 left。

动图展示:

这个从右开始取 tmp 值,所以从左先开始找,效果一样。

1.3.3前后指针:

具体思路:先选定左边第一个为 tmp ,同时设定 left 位置为 prev,prev 的后一个位置为 cur,从a[cur] 开始和 tmp 比较,如果 a[cur] 比 tmp 小,就先将 prev++,再让 a[cur] 和 a[prev] 交换,然后 cur++,如果 a[cur] 比 tmp大,那么就不改变 prev,也不用交换,只对 cur++,以此类推,直到cur>right 就结束,最后将 a[prev] 和 tmp 交换就完成了一趟快排。

动图展示:

1.4代码实现:

1.4.1hoare版本:
// 快速排序hoare版本
int PartSort1(int* arr, int left, int right)
{//优化1:三数取中int mid = chose_middle(arr, left, right);Swap(&arr[left], &arr[mid]);//交换两个位置值的函数int key = left;int begin = left;int end = right;while (begin < end){//右边找小while (begin < end && arr[end] >= arr[key]){end--;}//左边找大while (begin < end && arr[begin] <= arr[key]){begin++;}Swap(&arr[begin], &arr[end]);//交换两个位置值的函数}Swap(&arr[key], &arr[begin]);//交换两个位置值的函数return begin;
}void Quick_sort(int* arr, int left, int right)
{// 快速排序hoare版本//int key = PartSort1(arr, left, right);Quick_sort(arr, left, key - 1);Quick_sort(arr, key + 1, right);
}
1.4.2挖坑法:
// 快速排序挖坑法
int PartSort2(int* arr, int left, int right)
{//优化1:三数取中//int mid = chose_middle(arr, left, right);//Swap(&arr[left], &arr[mid]);int pit = left;int key = arr[left];int begin = left;int end = right;while (begin < end){while (arr[end] > key && begin < end){end--;}Swap(&arr[end], &arr[pit]);pit = end;while (arr[begin] < key && begin < end){begin++;}Swap(&arr[begin], &arr[pit]);pit = begin;Print_arr(arr, 9);printf("\n");}Swap(&arr[pit], &key);return begin;
}void Quick_sort(int* arr, int left, int right)
{// 快速排序挖坑法int key = PartSort2(arr, left, right);Quick_sort(arr, left, key - 1);Quick_sort(arr, key + 1, right);
}
1.4.3前后指针:
// 快速排序前后指针法
int PartSort3(int* arr, int left, int right)
{//优化1:三数取中int mid = chose_middle(arr, left, right);Swap(&arr[left], &arr[mid]);int key = left;int prev = left;int cur = prev + 1;while (cur <= right){if (arr[cur] < arr[key] && ++prev != cur)Swap(&arr[cur], &arr[prev]);cur++;}Swap(&arr[prev], &arr[key]);return prev;
}void Quick_sort(int* arr, int left, int right)
{// 快速排序前后指针法int key = PartSort3(arr, left, right);Quick_sort(arr, left, key - 1);Quick_sort(arr, key + 1, right);
}

2.计数排序

2.1基本思想:

计数排序在于将输入的数据值转化为键存储在额外开辟的数组空间中。用新数组对应下标位置的值来表示该数在原数组出现的次数,计数排序要求输入的数据必须是有确定范围的整数。

2.2基本思路:

 (1)找出待排序数组中的最大值和最小值:这是为了确定计数数组(或称为桶)的大小和范围。计数数组的大小通常是最大值与最小值之差加1(如果包含负数,则需要调整以包含所有可能的值)。

 (2)创建计数数组并初始化:根据步骤1确定的范围,创建一个新的数组,用于记录每个元素在原数组中出现的次数。这个数组的所有元素初始化为0。

(3)统计每个元素的出现次数:遍历原数组,对于每个元素,将其值作为计数数组的索引(如果元素是负数或范围超出常规,则需要进行适当的调整,如加上一个偏移量),并将该索引位置上的值加1。这样,计数数组的每个位置就记录了原数组中对应元素的出现次数。

(4)对计数数组进行累加(可选,但有助于后续排序):从计数数组的第一个元素开始,向后遍历,将每个元素的值更新为当前元素与前一个元素之和。这一步是为了确定每个元素在排序后数组中的位置。例如,如果某个元素在原数组中出现了n次,并且它是第k个不同的元素,那么它在排序后的数组中应该占据从第k个位置开始的n个连续位置

(5)根据计数数组重构原数组:这是排序的最后一步。从后向前遍历原数组(或者从后向前遍历计数数组,同时遍历一个指针指向排序后数组的末尾),对于原数组中的每个元素,找到它在计数数组中的索引,并根据计数数组的值(或者累加后的值)确定它在排序后数组中的位置,然后将该元素放到排序后数组的相应位置上,并减少计数数组中对应索引的值(如果进行了累加)。这个过程可能需要从后向前遍历原数组,以确保稳定性(即相等元素的相对顺序不变)。

(6)处理负数或特殊范围:如果原数组中包含负数或元素范围超出了常规整数范围,可能需要在步骤1和步骤2中进行适当的调整,如加上一个偏移量,以确保所有元素都能被正确地映射到计数数组的索引上。

2.3图解过程:

  

动图展示:

  

2.4代码实现:

// 计数排序
void CountSort(int* arr, int n)
{int min = arr[0];int max = arr[0];for (int i = 1; i < n; i++){if (arr[i] < min){min = arr[i];}if (arr[i] > max){max = arr[i];}}//创建一个数组来计算(相对)下标出现的次数int count = max - min + 1;//calloc会自动初始化数组为0//int* tmp = (int*)calloc(count,sizeof(int));//if (tmp == NULL)//{//	perror("calloc error");//	return;//}//使用malloc时需要初始化数组int* tmp = (int*)malloc(count * sizeof(int));if (tmp == NULL){perror("malloc error");return;}for (int i = 0; i < count; i++){tmp[i] = 0;}//把原数组映射到开辟的数组中for (int i = 0; i < n; i++){tmp[arr[i] - min]++;}//排序int j = 0;for (int i = 0; i < count; i++){while (tmp[i]--){arr[j] = i + min;j++;}}free(tmp);tmp = NULL;
}

3.归并排序

3.1基本思想:

归并排序基本思想是将一个数组分成两半,对每半部分递归地应用归并排序,然后将排序好的两半合并在一起。这个过程一直递归进行,直到数组被分割成只包含一个元素的子数组,这些子数组自然就是排序好的。然后,算法开始合并这些子数组,直到合并成一个完整的排序好的数组。

3.2基本思路:

(1)分解:将数组分解成两个较小的子数组,直到子数组的大小为1。

(2)递归进行排序并合并:递归地对子数组进行归并排序,并将已排序的子数组合并成一个大的有序数组,直到合并为1个完整的数组

3.3图解过程:

3.4代码实现:

void _Merge_sort(int* arr, int* arr1, int left, int right)
{if (left >= right){return;}//划分区间int key = (left + right) / 2;//[left,key],[key+1,right]_Merge_sort(arr, arr1, left, key);_Merge_sort(arr, arr1, key + 1, right);//归并int left1 = left;int right1 = key;int left2 = key + 1;int right2 = right;int i = left;while (left1 <= right1 && left2 <= right2){if (arr[left1] < arr[left2]){arr1[i] = arr[left1];i++;left1++;}else{arr1[i] = arr[left2];i++;left2++;}}while (left1 <= right1){arr1[i] = arr[left1];i++;left1++;}while (left2 <= right2){arr1[i] = arr[left2];i++;left2++;}//交换完一次拷贝一次memcpy(arr + left, arr1 + left, (right - left + 1) * sizeof(int));
}//归并排序(递归)
void Merge_sort(int* arr, int n)
{int* arr1 = (int*)malloc(sizeof(int) * n);if (arr1==NULL){perror("malloc error");return;}_Merge_sort(arr, arr1, 0, n - 1);free(arr1);arr1 = NULL;
}

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

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

相关文章

IPv4地址和子网掩码

IP地址构成&#xff1a; IP 地址由 4 组 8 位二进制组成的&#xff0c;一共 32 位。 网络号和主机号&#xff1a; IP 地址由网络号和主机号组成。和第二张图片上一样&#xff0c;前面相同标蓝的就是网络号&#xff0c;不同的就是主机号。不同网络的通信需要通过路由器连接&…

Python简介、发展史

Python简介、发展史 本文目录&#xff1a; 零、时光宝盒 一、Python简介 二、Python设计者 三、Python发展史 四、Python语言的编程语言特性 五、Python现状 六、Python的未来 零、时光宝盒 我家所在的楼是3栋楼连接在一起的建筑&#xff0c;也就是3栋楼楼顶建筑上互通。…

mysql的半同步模式

1.半同步模式原理 mysql的主备库通过binlog日志保持一致&#xff0c;主库本地执行完事务&#xff0c;binlog日志落盘后即返回给用户&#xff1b;备库通过拉取主库binlog日志来同步主库的操作。默认情况下&#xff0c;主库与备库并没有严格的同步&#xff0c;因此存在一定的概率…

linux下一切皆文件,如何理解?

linux下一切皆文件&#xff0c;不管你有没有学过linux&#xff0c;都应该听过这句话&#xff0c;就像java的一切皆对象一样。 今天就来看看它的真面目。 你记住了&#xff0c;只要一个竞争退出它的PCB要被释放文件名&#xff0c;客服表也要被释放。那么&#xff0c;指向这个文件…

第100+23步 ChatGPT学习:概率校准 Sigmoid Calibration

基于Python 3.9版本演示 一、写在前面 最近看了一篇在Lancet子刊《eClinicalMedicine》上发表的机器学习分类的文章&#xff1a;《Development of a novel dementia risk prediction model in the general population: A large, longitudinal, population-based machine-learn…

0.0 C语言被我遗忘的知识点

文章目录 位移运算(>>和<<)函数指针函数指针的应用场景 strcmp的返回值合法的c语言实数表示sizeof 数组字符串的储存 —— 字符数组与字符指针字符串可能缺少 \0 的情况 用二维数组储存字符串数组其他储存字符串数组的方法 位移运算(>>和<<) 右移(>…

c++中的匿名对象及内存管理

c中的匿名对象 A a;//a的生命周期在整个main函数中 a.Sum(1); //匿名对象生命周期只有一行&#xff0c;只有这一行会创建对象,出了这一行就会调析构 A().Sum(1);//只有这一行需要这个对象&#xff0c;其他地方不需要。 return 0; 日期到天数的转换 计算日期到天数转换_牛客…

【鸿蒙样式初探】多个组件如何共用同一样式

最近开发鸿蒙&#xff0c;刚接触难免二和尚摸不着头脑&#xff0c;尤其是样式...... 背景 在做银行卡显示的一个小需求时&#xff1a; 每个Text都需要设置fontColor:#FFFFFF" 想着是否可以简单点 解决历程 思路一&#xff1a;&#xff08;拒绝) 使用Styles 提取封装公…

爆改YOLOv8|利用可改变核卷积AKConv改进yolov8-轻量涨点

1&#xff0c;本文介绍 AKConv&#xff08;可改变核卷积&#xff09;是一种改进的卷积操作方法&#xff0c;其核心在于动态调整卷积核的形状和大小。与传统卷积层固定核大小不同&#xff0c;AKConv 通过引入可学习的机制&#xff0c;使卷积核在训练过程中能够自适应地调整&…

学生宿舍管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;宿舍公告管理&#xff0c;学生管理&#xff0c;宿舍管理&#xff0c;后勤人员管理&#xff0c;楼栋信息管理&#xff0c;宿舍分配管理管理&#xff0c;退宿信息管理 微信端账号功能包括&#xff1a;系…

程序猿成长之路之数据挖掘篇——Kmeans聚类算法

Kmeans 是一种可以将一个数据集按照距离&#xff08;相似度&#xff09;划分成不同类别的算法&#xff0c;它无需借助外部标记&#xff0c;因此也是一种无监督学习算法。 什么是聚类 用官方的话说聚类就是将物理或抽象对象的集合分成由类似的对象组成的多个类的过程。用自己的…

idea import配置

简介 本文记录idea中import相关配置&#xff1a;自动导入依赖、自动删除无用依赖、避免自动导入*包 自动导入依赖 在编辑代码时&#xff0c;当只有一个具有匹配名称的可导入声明时&#xff0c;会自动添加导入 File -> Settings -> Editor -> General -> Auto Imp…

简而不减,极致便捷!泰极预付费解决方案震撼上市

开户麻烦!绑表复杂!用电情况模糊!电费收缴难! 在日常生活中,能源缴费可能经常会遇到运维难管理、缴费收益难计算、支付安全难保障等问题。如何解决呢?正泰物联推出“泰极预付费解决方案”,“简”操作,“不减”功能,有效解决上述问题,助力实现便捷生活。 享轻松:泰极简而不减…

MySQL内部临时表(Using temporary)案例详解及优化解决方法

目录 前言 一.场景案例 二、什么是内部临时表&#xff1f; 三、哪些场景会使用内部临时表&#xff1f; 四、内部临时表如何存储&#xff1f; 1&#xff09;使用内存 2&#xff09;先使用内存&#xff0c;再转化成磁盘文件 3&#xff09;直接使用磁盘文件 五、如何优化…

【软件文档】项目总结报告编制模板(Word原件参考)

1. 项目概要 1.1. 项目基本信息 1.2. 项目期间 1.3. 项目成果 1.4. 开发工具和环境 2. 项目工作分析 2.1. 项目需求变更 2.2. 项目计划与进度实施 2.3. 项目总投入情况 2.4. 项目总收益情况 2.5. 项目质量情况 2.6. 风险管理实施情况 3. 经验与教训 3.1. 经验总结…

【异常错误】pycharm可以在terminal中运行,但是无法在run中运行(没有输出错误就停止了)

问题&#xff1a; pycharm的命令可以在terminal中运行&#xff0c;但是复制到无法在run中运行&#xff08;没有输出错误就停止了&#xff09; run中运行后什么错误提示都没有 搞不懂为什么 解决&#xff1a; 降低run中batch-size的大小&#xff0c;即可以运行 我并没有观察到…

Unity(2022.3.41LTS) - 后处理

目录 一、什么是后处理 二、后处理的工作原理 三、后处理的常见效果 四、如何在 Unity 中实现后处理 五、后处理的性能影响 六. 详细效果 一、什么是后处理 后处理是在场景渲染完成后&#xff0c;对最终图像进行的一系列操作。这些操作可以包括调整颜色、添加特效、模糊…

Windows Geth1.14.3私链搭建

geth下载官网&#xff1a;Downloads | go-ethereum 安装完成的目录 安装完后配置环境变量&#xff0c;在终端输入geth version 第一步&#xff1a;第一种创建账户方式geth account new --keystore keystore 创建一个账户&#xff0c;在当前目录下创建一个keystore的子目录&…

Linux工具使用

Linux编辑器-vim使用 1.vim的基本概念 在vim中&#xff0c;主要的三种模式分别是命令模式&#xff0c;插入模式和底行模式。 正常/普通/命令模式(Normal mode) 控制屏幕光标的移动&#xff0c;字符、字或行的删除&#xff0c;移动复制某区段及进入Insert mode下&#xff0c;…

一本读懂数据库发展史的书

数据库及其存储技术&#xff0c;一直以来都是基础软件的主力。数据库系统的操作接口标准&#xff0c;也是应用型软件的重要接口&#xff0c;关系重大。 作为最“有感”的系统软件&#xff0c;数据库的历史悠久、品类繁多、创新活跃。 对数据库历史发展的介绍&#xff0c;有利…