七大排序算法

目录

直接插入排序

希尔排序

直接选择排序

堆排序

冒泡排序

快速排序

快速排序优化

非递归实现快速排序

归并排序

非递归的归并排序


排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.

常见的排序算法有插入排序(直接插入排序和希尔排序),选择排序(选择排序和堆排序),交换排序(冒泡排序和快速排序)以及归并排序.

我们将从时间复杂度,空间复杂度,以及排序的稳定性来分析这七大排序.

排序的稳定性

假定在待排序的记录序列中,存在多个具有相同关键字的记录,若经过排序,这些记录的相对次序保持不变,则称这种排序是稳定的.

直接插入排序

基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到 一个新的有序序列

public static void insertSort(int[] array){for (int i = 1; i < array.length; i++) {int tmp = array[i];int j = i-1;for (; j >= 0 ; j--) {if (array[j] > tmp){array[j+1] = array[j];}else {break;}}array[j+1] = tmp;}}

时间复杂度:考虑最坏的情况下,就是全逆序的时候,此时时间复杂度为O(N^2).

最好的情况下,有序的时候,此时时间复杂度为O(N).得出一个结论:当数据量不多,且数据基本上是趋于有序的时候,此时直接插入排序是非常快的.

空间复杂度:O(1)

稳定性:稳定.一个本身就稳定的排序,可以实现为不稳定的排序;但是一个本身不稳定的排序,不能实现为稳定的排序.


希尔排序

希尔排序(缩小增量排序)是直接插入排序的一个优化.

基本思想:先将数据进行分组,将每一组内的数据先进行排序(这一过程叫做预排序);逐渐缩小组数,直到最后整体看作是一组,采用直接插入排序进行排序.

跳着分组的原因:尽可能的使小的数据靠前,大的数据靠后,从而使整体更加趋于有序.

public static void shellSort(int[] array){int gap = array.length;while (gap > 1){gap /= 2;shell(array,gap);}}private static void shell(int[] array, int gap) {for (int i = gap; i < array.length; i++) {int tmp = array[i];int j = i-gap;for (; j >= 0 ; j-=gap) {if (array[j] > tmp){array[j+gap] = array[j];}else {break;}}array[j+gap] = tmp;}}

当gap>1时,都是预排序,目的是让数组更接近于有序.当gap==1时,此时数组已经接近有序了,这样进行插入排序就会很快.

希尔排序的时间复杂度不好计算,因为gap的取值方法有很多,导致很难去计算.目前还没有证明gap具体取多少是最快的.

时间复杂度:O(N^1.3),估计的时间复杂度.

空间复杂度:O(1)

稳定性:不稳定.


直接选择排序

基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放到序列的起始位置,直到全部排序的数据元素排完.

public static void selectSort(int[] array){for (int i = 0; i < array.length; i++) {int minIndex = i;for (int j = i+1; j < array.length; j++) {if (array[minIndex] > array[j]){minIndex = j;}}//处理两个下标一样的情况if (i != minIndex) {int tmp = array[i];array[i] = array[minIndex];array[minIndex] = tmp;}}}

直接选择排序好理解,但是效率低下.

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

空间复杂度:O(1)

稳定性:不稳定


堆排序

排升序要建大堆,排降序建小堆.

升序,建大堆:堆顶元素和最后一个元素交换,将数组长度-1,在对堆顶元素进行向下调整,依次循环.

public static void heapSort(int[] array){createBigHeap(array);int end = array.length-1;while(end > 0){swap(array,0,end);shiftDown(array,0,end);end--;}}//建立大根堆//从最后一颗子树的根节点开始,每一次都进行向下调整private static void createBigHeap(int[] array){for (int parent = (array.length-1-1)/2; parent >=0 ; parent--) {shiftDown(array,parent,array.length);}}//向下调整private static void shiftDown(int[] array,int parent,int len){int child = (2*parent)+1;while (child < len){if (child+1 < len && array[child] < array[child+1]){child++;}if (array[child] > array[parent]){swap(array,child,parent);parent = child;child = (2*parent)+1;}else {break;}}}public static void swap(int[] array,int x,int y){int tmp = array[x];array[x] = array[y];array[y] = tmp;}

时间复杂度:O(n*logn)

空间复杂度:O(1)

稳定性:不稳定


冒泡排序

相邻元素之间的比较.

public static void bubbleSort(int[] array){//最外层控制的是趟数//五个数据需要比较四趟for (int i = 0; i < array.length-1; i++) {//加一个标志对冒泡排序进行优化boolean flg = false;for (int j = 0; j < array.length - 1 - i; j++) {if (array[j] > array[j+1]){swap(array,j,j+1);flg = true;}}if (flg == false){break;}}}

时间复杂度:(不考虑优化)O(n^2)

空间复杂度:O(1)

稳定性:稳定


快速排序

快速排序是Hoare 1962 年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
public static void quickSort(int[] array){quick(array,0,array.length-1);}public static void quick(int[] array,int start,int end){//大于号 是预防1 2 3 4 5 6,直接没有左树或没有右树if (start >= end){return;}int pivot = partition(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}//找基准//Hoare版本private static int partition(int[] array,int left,int right){int i = left;int pivot = array[left];while (left < right){while (left < right && array[right] >= pivot){right--;}//right下标的值小于pivotwhile (left < right && array[left] <= pivot){left++;}//left下标的值大于pivotswap(array,left,right);}//循环走完,left和right相遇//交换 和原来的leftswap(array,left,i);//返回基准return left;}

时间复杂度:O(n*logn)

此时间复杂度不是最坏情况下的时间复杂度,最坏情况下是有序的情况下,此时树的高度是n,时间复杂度是O(n^2),空间复杂度也变成了O(n).

空间复杂度:O(logn)

稳定性:不稳定

挖坑法找基准

private static int partition(int[] array,int left,int right){int pivot = array[left];while (left < right){while (left < right && array[right] >= pivot){right--;}array[left] = array[right];while (left < right && array[left] <= pivot){left++;}array[right] = array[left];}array[left] = pivot;//返回基准return left;}

挖坑法是找到合适的值直接交换.

需要注意的是挖坑法和hoare找基准的结果是不一样的但是最终都是有序的.


快速排序优化

当数据有序的时候,快速排序的时间复杂度达到最大,而且空间复杂度也会随之改变.

三数取中法

为了使二叉树的划分尽可能的均匀,我们在left,mid,right三个数中,取出中间大的值,来作为key(left).

比如1 2 3 4 5,如果不采用三数取中法,那么1作为key走下来,left和right在1相遇,基准就是1,此时划分的就是单分支的树;如果采用三数取中,将数组顺序调整为 3 2 1 4 5,3作为key,走下来,left和right在中间位置相遇,将3和1交换,变为 1 2 3 4 5,虽然又变回去了,但是此时的基准在中间位置3的地方,此时二叉树划分的将更加均匀.

public static void quick(int[] array,int start,int end){//大于号 是预防1 2 3 4 5 6,直接没有左树或没有右树if (start >= end){return;}//在找基准之前,进行三数取中的优化,尽量去解决划分不均匀的问题.//在left,mid,right三个数中找到中间大的数字做keyint index = findMidValueOfIndex(array, start, end);swap(array,start,index);int pivot = partitionHoare(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}//3个数中取中位数private static int findMidValueOfIndex(int[] array,int start,int end){int minIndex = (start+end)/2;if (array[start] > array[end]){if (array[minIndex] > array[start]){return start;}else if (array[minIndex] < array[end]){return end;}else {return minIndex;}}else {if (array[minIndex] > array[end]){return end;}else if (array[minIndex] < array[start]){return start;}else {return minIndex;}}}

我们除了采取这种优化之外,还可以在快速排序递归到小区间的时候,采用插入排序.

因为插入排序在数据趋于有序并且数据量小的时候,排序的速度非常快.


非递归实现快速排序

 public static void quickSort2(int[] array) {Stack<Integer> stack = new Stack<>();int start = 0;int end = array.length-1;int pivot = partition(array,start,end);//1.判断左边是不是有2个元素if(pivot > start+1) {stack.push(start);stack.push(pivot-1);}//2.判断右边是不是有2个元素if(pivot < end-1) {stack.push(pivot+1);stack.push(end);}while (!stack.isEmpty()) {end = stack.pop();start = stack.pop();pivot = partition(array,start,end);//3.判断左边是不是有2个元素if(pivot > start+1) {stack.push(start);stack.push(pivot-1);}//4.判断右边是不是有2个元素if(pivot < end-1) {stack.push(pivot+1);stack.push(end);}}

归并排序

先分解,后合并.

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并.

public static void mergeSort1(int[] array) {mergeSortChild(array,0,array.length-1);}private static void mergeSortChild(int[] array,int left,int right) {if(left == right) {return;}int mid = (left+right) / 2;mergeSortChild(array,left,mid);mergeSortChild(array,mid+1,right);//合并merge(array,left,mid,right);}private static void merge(int[] array,int left,int mid,int right) {int s1 = left;int e1 = mid;int s2 = mid+1;int e2 = right;int[] tmpArr = new int[right-left+1];int k = 0;//表示tmpArr 的下标while (s1 <= e1  && s2 <= e2) {if(array[s1] <= array[s2]) {tmpArr[k++] = array[s1++];}else{tmpArr[k++] = array[s2++];}}while (s1 <= e1) {tmpArr[k++] = array[s1++];}while (s2 <= e2) {tmpArr[k++] = array[s2++];}//tmpArr当中 的数据 是right  left 之间有序的数据for (int i = 0; i < k; i++) {array[i+left] = tmpArr[i];}}

时间复杂度:O(n*logn).

空间复杂度:O(n)

稳定性:稳定


非递归的归并排序

 public static void mergeSort(int[] array) {int gap = 1;while (gap < array.length) {for (int i = 0; i < array.length; i += gap*2) {int left = i;int mid = left + gap -1;int right = mid+gap;if(mid >= array.length) {mid = array.length-1;}if(right >= array.length) {right = array.length-1;}merge(array,left,mid,right);}gap *= 2;}}

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

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

相关文章

工具 | XShell的学习与使用

工具 | XShell的学习与使用 时间&#xff1a;2023年9月8日09:03:29 文章目录 工具 | XShell的学习与使用1.下载2.安装 1.下载 1.官网XSHELL - NetSarang Website 2.免费版下载&#xff1a;家庭/学校免费 - NetSarang Website (xshell.com) 3.https://cdn.netsarang.net/de06d10…

Postman接口测试流程

一、工具安装 ● 安装Postman有中文版和英文版&#xff0c;可以选择自己喜欢的版本即可。安装时重新选择一下安装路径&#xff08;也可以默认路径&#xff09;&#xff0c;一直下一步安装完成即可。&#xff08;本文档采用英文版本&#xff09;安装文件网盘路径链接&#xff1…

transformer 总结(超详细-初版)

相关知识链接 attention1attention2 引言 本文主要详解 transformer 的算法结构以及理论解释&#xff0c;代码实现以及具体实现时候的细节放在下一篇来详述。 下面就通过上图中 transformer 的结构来依次解析 输入部分(Encode 侧) input 输出主要包含 两个部分&#xff1a…

第5篇 vue的通信框架axios和ui框架-element-ui以及node.js

一 axios的使用 1.1 介绍以及作用 axios是独立于vue的一个项目&#xff0c;基于promise用于浏览器和node.js的http客户端。 在浏览器中可以帮助我们完成 ajax请求的发送在node.js中可以向远程接口发送请求 1.2 案例使用axios实现前后端数据交互 1.后端代码 2.前端代码 &…

微信最新更新隐私策略(2023-08-15)

1、manifest.json 配置修改 在mp-weixin: 参数修改&#xff08;没有就添加&#xff09; "__usePrivacyCheck__": true, ***2、注意 微信开发者工具调整 不然一直报错 找不到 getPrivacySetting 废话不多说 上代码 3、 编辑首页 或者用户授权界面 <uni-popup…

【云原生】Kubeadmin部署Kubernetes集群

目录 ​编辑 一、环境准备 1.2调整内核参数 二、所有节点部署docker 三、所有节点安装kubeadm&#xff0c;kubelet和kubectl 3.1定义kubernetes源 3.2开机自启kubelet 四、部署K8S集群 4.1查看初始化需要的镜像 4.2在 master 节点上传 v1.20.11.zip 压缩包至 /opt 目录…

【Linux】文件系统

磁盘及文件系统 文件的增删查改 重新认识目录 目录是文件嘛&#xff1f; 是的。 目录有iNode嘛&#xff1f; 有 目录有内容嘛&#xff1f; 有 任何一个文件&#xff0c;一定在一个目录内部&#xff0c;所以一个目录的内容是什么&#xff1f; 需要数据块&#xff0c;目录的数据…

【技术支持案例】S32K146的hard fault问题处理

文章目录 1. 案例背景2. 方案准备2.1 HardFault&#xff08;硬件错误异常&#xff09;2.2 UsageFault&#xff08;用法错误异常&#xff09;2.3 BusFault&#xff08;总线错误异常&#xff09;2.4 MemManage Fault&#xff08;存储器管理错误异常&#xff09; 3. 现场支持3.1 现…

Java基础之static关键字

目录 静态的特点第一章、静态代码块第二章、静态属性第三章、静态方法调用静态方法时静态方法中调用非静态方法时 第四章、static关键字与其他关键字 友情提醒 先看文章目录&#xff0c;大致了解文章知识点结构&#xff0c;点击文章目录可直接跳转到文章指定位置。 静态的特点…

JVM学习(一)--程序计数器

作用&#xff1a;记住下一个jvm指令的执行地址 每一行java源代码&#xff0c;会被编译为多行jvm指令&#xff0c;上文所说的执行地址就是这里的0,3,4等 &#xff0c;由于执行访问特别频繁&#xff0c;程序计数器的底层是有寄存器来实现的 特点&#xff1a; 线程私有&#xff…

kafka学习-生产者

目录 1、消息生产流程 2、生产者常见参数配置 3、序列化器 基本概念 自定义序列化器 4、分区器 默认分区规则 自定义分区器 5、生产者拦截器 作用 自定义拦截器 6、生产者原理解析 1、消息生产流程 2、生产者常见参数配置 3、序列化器 基本概念 在Kafka中保存的数…

一文速学-让神经网络不再神秘,一天速学神经网络基础(七)-基于误差的反向传播

前言 思索了很久到底要不要出深度学习内容&#xff0c;毕竟在数学建模专栏里边的机器学习内容还有一大半算法没有更新&#xff0c;很多坑都没有填满&#xff0c;而且现在深度学习的文章和学习课程都十分的多&#xff0c;我考虑了很久决定还是得出神经网络系列文章&#xff0c;…

Linux安装kibana

相关链接 https://www.elastic.co/cn/downloads/kibana https://artifacts.elastic.co/downloads/kibana/kibana-7.5.1-linux-x86_64.tar.gz 官网下载可能比较慢&#xff0c;下面提供下载地址 百度云链接&#xff1a;https://pan.baidu.com/s/1d9Cqr9EwHF94op90F57bww 提取码…

Elasticsearch实战(五):Springboot实现Elasticsearch电商平台日志埋点与搜索热词

文章目录 系列文章索引一、提取热度搜索1、热搜词分析流程图2、日志埋点&#xff08;1&#xff09;排除logback的默认集成。&#xff08;2&#xff09;引入log4j2起步依赖&#xff08;3&#xff09;设置配置文件&#xff08;4&#xff09;配置文件模板&#xff08;5&#xff09…

计算机竞赛 基于深度学习的人脸性别年龄识别 - 图像识别 opencv

文章目录 0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程 4 具体实现4.1 预训练数据格式4.2 部分实现代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 毕业设计…

使用 【jacoco】对基于 SpringBoot 和 Dubbo RPC 的项目生成测试覆盖率报告:实践+原理

基于 Dubbo RPC 的项目中有一个提供者项目backend、一个消费者项目gateway、以及注册中心nacos。本篇文章记录在windows本地对该框架的测试过程&#xff0c;以及介绍jacoco的基本原理 测试过程 官网下载安装包解压到本地&#xff0c;https://www.jacoco.org/jacoco/ 只需要用…

vue.js+nodejs家庭个人理财收支管理系统5x6nf

本收支管理系统以vue.js作为框架&#xff0c;nodejs语言&#xff0c;B/S模式以及MySql作为后台运行的数据库。本系统主要包括以下功能模块&#xff1a;用户管理、收入分类、支出分类、每日收入、每日支出等模块。 本文的组织结构如下&#xff1a; 1、绪论。综述了本文的研究背景…

Redis快速入门

文章目录 0. Redis介绍1. Centos下Redis安装2. redis.conf配置文件介绍3. redis相关命令4. 启动3.2 **命令行操作**3.3 Redis压测命令 4.redis中发布订阅和事务4.1 发布订阅&#xff08;Pub/Sub&#xff09;4.2 事务 5. redis封装系统服务6. 问题与解决6.1 启动Redis报错&#…

荣耀崛起阵容推荐,荣耀崛起最强阵容

今天给大家带来的荣耀崛起阵容推荐是新手阵容推荐&#xff0c;以核心输出为点&#xff0c;由点及面&#xff0c;来展开叙述阵容&#xff01; 关注【娱乐天梯】&#xff0c;获取荣耀崛起0.1折内部福利号 荣耀崛起最强阵容兽族战神流&#xff1a; 此阵容是以战士为核心&#xff0…

机器学习_特征工程_特征数据的评价标准

本文主要从 单特征分析&#xff0c;多特征筛选&#xff0c;特征监控&#xff0c;外部特征评估的几个方面对特征数据进行阐述。 来源 &#xff1a; 特征筛选_特征覆盖度怎么算_adamyoungjack的博客-CSDN博客 1. 单特征分析 1.1 简介 好特征可以从几个角度衡量&#xff1a;覆…