排序算法见解(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栋楼楼顶建筑上互通。…

react中的useCallback、useMemo、useRef 和 useContext

hook函数中依赖项&#xff1a;函数中使用的响应式变量组成的数组。响应式变量包括 props、state 和所有你直接在组件中定义的变量和函数。 前言 一、useCallback 缓存回调函数 使用方式 二、useMemo&#xff1a;缓存计算的结果 三、useRef&#xff1a;在多次渲染之间共享…

Spring MVC概述

1.1 MVC设计模式 MVC&#xff08;Model-View-Controller&#xff09;是一种软件设计模式&#xff0c;旨在将应用程序的业务逻辑、用户界面和用户输入分离。Spring MVC遵循这一模式&#xff0c;提供了以下几个核心组件&#xff1a; Model&#xff1a;表示应用程序的数据和业务…

sqlite3 数据库

1.sqlite3 相关命令&#xff1a; .tables 查看数据库中的表 .headers on/off 开启或者关闭表头 .width 设置列宽 .mode column 对齐 .schema 查询表头类型 2.sqlite3 的sql语句&#xff1a; 插入数据&#xff1a;insert into 表名 values; 查询表&#xff1a;select …

mysql的半同步模式

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

ocp19c 学习第1篇

1.操作系统镜像&#xff0c;安装学习环境 通过网盘分享的文件&#xff1a;12-V995537-01.iso 链接: https://pan.baidu.com/s/1nYeKMSM-gwsJon9kpCs_Fw?pwd5537 提取码: 5537 2.linux7防火墙设置 linux7防火墙设置[rootocp4 ~]# systemctl status firewalld.service[rootoc…

【C++】如何解决“pointer to incomplete class type is not allowed”。

这个错误信息 “pointer to incomplete class type is not allowed” 在 C 中通常表示你正在尝试使用一个尚未完全定义的类的指针。 可能的原因及解决方法如下&#xff1a; 一、类定义不完整 前向声明后就使用指针&#xff1a; 如果你只是对一个类进行了前向声明&#xff08…

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; 日期到天数的转换 计算日期到天数转换_牛客…

Linux不可靠信号和可靠信号

1.不可靠信号和可靠信号 建立在早期的信号处理机制上**(1~31)的信号是不可靠信号** 不支持排队、可能会丢失&#xff0c;如果同一个信号连续产生多次&#xff0c;进程可能只相应了一次 如果解除屏蔽&#xff0c;取决于可靠性&#xff01;&#xff01;&#xff01;1 建立在新的信…

pytest自定义命令行选项

在 pytest 中&#xff0c;您可以通过多种方式自定义命令行选项。以下是一些常用的方法&#xff1a; 使用 pytest 的 addoption 方法 您可以在 conftest.py 文件中使用 pytest_addoption 钩子函数来添加自定义命令行选项。 下面是一个示例&#xff0c;展示如何添加一个名为 --…

python 执行mysql文件

MySQL 5.7 之前的版本在某些方面对存储过程的支持可能有限&#xff0c;通过 Python 脚本调用 SQL 文件是一种有效的方法来自动化数据库任务和提高运维效率。具体操作如下&#xff1a; # python 执行sql脚本 def execute_sql_script(mysql_params, script_name):# 使用 with 语…

硬件调试经验积累 关于RTC 时钟问题。

1. 电脑的 RTC 问题可以排查有这几个地方 1. 硬件问题 2. BIOS 问题 3. 系统问题 2. 排查问题大致的操作 1. 使用计算系统xx 通讯读取 RTC 芯片的寄存器&#xff0c;查看芯片是否有问题。 2. 再BIOS 下查看时钟是否准确。 查看芯片的连接性是否有问题。 3..多次----断电后开机…

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

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

虚拟化、双层虚拟化、容器是什么,应用场景有哪些

一、基本概念 虚拟化是一种资源管理技术&#xff0c;通过将计算机的实体资源&#xff08;如CPU、内存、磁盘空间和网络适配器等&#xff09;进行抽象和转换&#xff0c;使其能够被分割、组合成一个或多个电脑配置环境。多层虚拟化则是在虚拟化的基础上&#xff0c;再进行一次虚…

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

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

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

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