时间复杂度接近O(n)的三种排序算法

1.桶排序

桶排序,顾名思义,会用到“桶”,核心思想是将要排序的数据分到几个有
序的桶里,每个桶内的数据再单独进行排序。桶内排完序之后,再把每个桶内的数据按照顺序依次
取出,组成的序列就是有序的了。

桶排序对要排序数据的要求是非常苛刻的。
首先,要排序的数据需要很容易就能划分成m个桶,并且,桶与桶之间有着天然的大小顺序。这样
每个桶内的数据都排序完之后,桶与桶之间的数据不需要再进行排序。

其次,数据在各个桶之间的分布是比较均匀的。如果数据经过桶的划分之后,有些桶内的数据非常
多,有些非常少,很不平均,那桶内数据排序的时间复杂度就不是常量级了。在极端情况下,如果
数据都被划分到一个桶内,那就退化为O(nlogn)的排序算法了。

桶排序比较适合用在外部排序中。所谓的外部排序就是数据存储在外部磁盘中,数据量比较大,内
存有限,无法将数据全部加载到内存中。
在这里插入图片描述

/**
 * 桶排序
 * 原地排序:否
 * 稳定排序:是
 * 空间复杂度:
 * 时间复杂度:O(n)
 */
public class BucketSort {
	public static void main(String[] args) {
		int[] arr = { 1, 45, 32, 23, 22, 31, 47, 24, 4, 15 };
		bucketSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	//存数区间0-9,10-19,20-29,30-39,40-49
	public static void bucketSort(int[] arr) {
		ArrayList bucket[] = new ArrayList[5];
		
		//初始化桶
		for(int i=0;i<bucket.length;i++) {
			bucket[i] = new ArrayList<Integer>();
		}
		
		//像桶内放入数据
		for(int i=0;i<arr.length;i++) {
			int index = arr[i]/10;
			bucket[index].add(arr[i]);
		}
		
		int index = 0;
		for(int i=0;i<bucket.length;i++) {
			bucket[i].sort(null);//对每个桶进行排序
			for(int j=0;j<bucket[i].size();j++) {
				arr[index++] = (int) bucket[i].get(j);
			}
		}
	}
}

2.计数排序

计数排序可以理解为是桶排序的一种特殊情况。当要排序的n个数据,所处的范围并不大的
时候,比如最大值是k,我们就可以把数据划分成k个桶。每个桶内的数据值都是相同的,省掉了桶
内排序的时间。

计数排序只能用在数据范围不大的场景中,如果数据范围k比要排序的数据n大很多,
就不适合用计数排序了。而且,计数排序只能给非负整数排序,如果要排序的数据是其他类型的,
要将其在不改变相对大小的情况下,转化为非负整数。
假定有原始数组A[8],它们分别是:2,5,3,0,2,3,0,3。数据范围从0-5

先用一个数组大小为6的数组C来存储在k值上数据有几个。
在这里插入图片描述

接着对数组顺序求和,C数组存储的数据就变成了,C[k]里存储小于等于分数k的的数据。
在这里插入图片描述

定义临时数组R,依次扫描数组原始数组A,将数据A入到R[C[k]]位置上,同时C[k]位置上的数据要减掉1,最后将R数组复制到原始数组A中。
在这里插入图片描述

/**
 * 计数排序
 * 原地排序:否
 * 稳定排序:是
 * 空间复杂度:O(k+n) k为数据范围大小
 * 时间复杂度:O(n+k)
 */
public class CountSort {
	public static void main(String[] args) {
		int[] arr = new int[]{5,3,1,3,2,8,6,9,10,4,6,4,8,10,7,4,2,1,6,7};
		CountingSort(arr,arr.length);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void CountingSort(int[] a,int n) {
		if(n<=-1) return;
		
		//查找数组中最大值
		int max = a[0];
		for(int i=1;i<a.length;i++) {
			if(max<a[i]) {
				max = a[i];
			}
		}
		
		//申请一个计数数组C下标为0到max
		int[] c = new int[max+1];
		for(int i=0;i<c.length;i++) {
			c[i] = 0;
		}
		
		//计算每个元素的个数,放入数组C中
		for(int i=0;i<a.length;i++) {
			c[a[i]]++;
		}
		
		//依次累加
		for(int i=0;i<max;i++) {
			c[i+1] = c[i]+c[i+1];
		}
		
		//临时数组R,存储排序之后的数组
		int[] r = new int[a.length];
		//计数排序的关键步骤
		for(int i=a.length-1;i>=0;i--) {
			int index = c[a[i]]-1;
			r[index] = a[i];
			c[a[i]]--;
		}
		
		//将结果拷贝给a数组
		for(int i=0;i<a.length;i++) {
			a[i] = r[i];
		}
	}
}

3.基数排序

先按照数据最后以位来排序,然后,再按照倒数第二位重新排序,以此类推,最后按照第一位重新排序。经过多次排序之后,数据就都有序了。如果数据长度不一致,需要补齐数据到相同长短。

基数排序对要排序的数据是有要求的,需要可以分割出独立的“位”来比较,而且位之间有递进的关系,如果a数据的高位比b数据大,那剩下的低位就不需要较了。除此之外,每一位的数据范围不能太大,才可以用线性排序算法来排序,否则,基数排序的时间复杂度就无法做到O(n)了。
在这里插入图片描述

/**
 * 基数排序
 * 原地排序:否
 * 稳定排序:是
 * 空间复杂度:O(d+n) k为数据范围大小
 * 时间复杂度:O(dn) d是维数
 */
public class RadixSort {
	public static void main(String[] args) {
		int[] arrs = new int[] {153,26,78,342,123,241,96};
		
		int max = getMaxData(arrs);
		
		for(int exp=1;max/exp>0;exp=exp*10) {
			CountingSort(arrs,arrs.length,exp);
			System.out.println(Arrays.toString(arrs));
		}
	}
	
	public static int getMaxData(int[] a) {
		//查找数组中最大值
		int max = a[0];
		for(int i=1;i<a.length;i++) {
			if(max<a[i]) {
				max = a[i];
			}
		}
		return max;
	}
	
	public static void CountingSort(int[] a,int n,int exp) {
		if(n<=-1) return;
		
		//查找数组中最大值
		int max = (a[0]/exp)%10;
		for(int i=1;i<a.length;i++) {
			if(max<(a[i]/exp)%10) {
				max = (a[i]/exp)%10;
			}
		}
		
		//申请一个计数数组C下标为0到max
		int[] c = new int[max+1];
		for(int i=0;i<c.length;i++) {
			c[i] = 0;
		}
		
		//计算每个元素的个数,放入数组C中
		for(int i=0;i<a.length;i++) {
			c[(a[i]/exp)%10]++;
		}
		
		//依次累加
		for(int i=0;i<max;i++) {
			c[i+1] = c[i]+c[i+1];
		}
		
		//临时数组R,存储排序之后的数组
		int[] r = new int[a.length];
		//计数排序的关键步骤
		for(int i=a.length-1;i>=0;i--) {
			int index = c[(a[i]/exp)%10]-1;
			r[index] = a[i];
			c[(a[i]/exp)%10]--;
		}
		
		//将结果拷贝给a数组
		for(int i=0;i<a.length;i++) {
			a[i] = r[i];
		}
	}

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CubeSLAM: Monocular 3D Object SLAM——论文简述

一、简介 提出一种在动态和静态环境中同时进行3D目标检测和定位建图的方法&#xff0c;并且能够互相提升准确度。具体地&#xff0c;对于3D目标&#xff0c;其位置、方向和尺寸通过slam进行了优化&#xff1b;而3D目标作为slam中的路标&#xff0c;可以提供额外的语义和几何约…

【统计学精要】:使用 Python 实现的统计检验— 1/10

一、介绍 欢迎来到“掌握 Python 统计测试&#xff1a;综合指南”&#xff0c;它将介绍本手册中您需要熟悉使用 Python 的所有基本统计测试和分析方法。本文将为您提供统计测试及其应用的全面介绍&#xff0c;无论您是新手还是经验丰富的数据科学家。 使用来自现实世界的实际示…

stable diffusion(1): webui的本地部署(windows)

一、前言 是的&#xff0c;现在是202308月份了&#xff0c;网上已经有很多打包好的工具&#xff0c;或者直接进一个web就能用SD的功能&#xff0c;但是我们作为程序员&#xff0c;就应该去躺坑&#xff0c;这样做也是为了能够有更多自主操作的空间。 像其他AI一样&#xff0c…

【链表OJ 3】链表的中间结点

前言: 本文收录于http://t.csdn.cn/n6UEP数据结构刷题的博客中,首先欢迎大家的来访&#xff0c;其次如有错误&#xff0c;非常欢迎大家的指正&#xff01;我会及时更正错误&#xff01; 目录 一.链表的中间结点 1.1原理:快慢指针的使用 链表元素个数为奇数时 链表元素个数…

SQL注入实操三(SQLilabs Less41-65)

文章目录 一、sqli-labs靶场1.轮子模式总结2.Less-41 stacked Query Intiger type blinda.注入点判断b.轮子测试c.获取数据库名称d.堆叠注入e.堆叠注入外带注入获取表名f.堆叠注入外带注入获取列名g.堆叠注入外带注入获取表内数据 3.Less-42 Stacked Query error baseda.注入点…

【小沐学C++】C++ 基于CMake构建工程项目(Windows、Linux)

文章目录 1、简介2、下载cmake3、安装cmake4、测试cmake4.1 单个源文件4.2 同一目录下多个源文件4.3 不同目录下多个源文件4.4 标准组织结构4.5 动态库和静态库的编译4.6 对库进行链接4.7 添加编译选项4.8 添加控制选项 5、构建最小项目5.1 新建代码文件5.2 新建CMakeLists.txt…

一、1.汇编指令、寄存器和寻址方式

立即数&#xff1a;可以立即在一条机器指令后找到具体数值的数&#xff0c;如内存中00位写着加指令&#xff0c;01位写着1100_1111&#xff0c;意思就是将1100_1111&#xff08;十进制207&#xff09;加到某处&#xff0c;反之可以表示数据的地址。 低端字节序&#xff1a;16位…

Java实现数字加密

Java实现数字加密 需求分析代码实现小结Time 需求分析 1.首先&#xff0c;考虑方法是否需要接收数据处理&#xff1f; 需要一个4位数&#xff0c;至于是哪一个数&#xff0c;让方法的调用者传递。 所以&#xff0c;方法的参数&#xff0c;就是这个需要加密的四位数 2.接着&…

nsqd的架构及源码分析

文章目录 一 nsq的整体代码结构 二 回顾nsq的整体架构图 三 nsqd进程的作用 四 nsqd启动流程的源码分析 五 本篇博客总结 在博客 nsq整体架构及各个部件作用详解_YZF_Kevin的博客-CSDN博客 中我们讲了nsq的整体框架&#xff0c;各个部件的大致作用。如果没看过的&…

【websocket - Tornado】简易聊天应用

1、背景 项目测试的过程中需要自己搭建一个webscoket站点,确保此类服务接入后台系统后访问不受影响。python的服务框架常用的有Flask、Django、Tornado,每个框架的侧重点不同,导致使用的场景就会有所差异。 Flask轻量级,采用常规的同步编程方式,需要安装其他模块辅助,主…

Pytest测试框架2

目录&#xff1a; pytest参数化用例pytest标记测试用例pytest设置跳过、预期失败用例pytest运行用例pytest测试用例调度与运行pytest命令行常用参数python执行pytestpytest异常处理 1.pytest参数化用例 参数化 通过参数的方式传递数据&#xff0c;从而实现数据和脚本分离。…

并网逆变器学习笔记6---三电平SVPWM下的连续和不连续调制

之前在学习中总结过一次DPWM策略选择&#xff1a;并网逆变器学习笔记5---三电平DPWM 但是对于三电平逆变器而言&#xff0c;如何从连续调制切换到不连续调制&#xff0c;存在一些疑惑点&#xff0c;下午闲来无事&#xff0c;把SVPWM下的连续调制和不连续调制的开关状态选择&am…

MyCat核心概念、需求案例讲解、环境准备及分片配置

1.MyCat概念介绍 2.MyCat入门需求 2.1 需求分析 2.2 环境准备 输入以下命令检查服务器防火墙状态 dead代表关闭状态&#xff0c;如果不关闭也可以需要开放特定的端口号&#xff01;&#xff01; systemctl status firewalld接着需要在三台服务器上的MySQL上创建三个数据库db0…

(树) 剑指 Offer 36. 二叉搜索树与双向链表 ——【Leetcode每日一题】

❓ 剑指 Offer 36. 二叉搜索树与双向链表 难度&#xff1a;中等 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个 排序的循环双向链表。要求不能创建任何新的节点&#xff0c;只能调整树中节点指针的指向。 为了让您更好地理解问题&#xff0c;以下面的二叉搜索树为…

相机传感器格式与镜头光圈参数

相机靶面大小 CCD/CMOS图像传感器尺寸&#xff08;sensor format&#xff09;1/2’‘、1/3’‘、1/4’实际是多大 1英寸——靶面尺寸为宽12.7mm*高9.6mm&#xff0c;对角线16mm。 2/3英寸——靶面尺寸为宽8.8mm*高6.6mm&#xff0c;对角线11mm。 1/2英寸——靶面尺寸为宽6.…

SSE技术和WebSocket技术实现即时通讯

文章目录 一、SSE1.1 什么是SSE1.2 工作原理1.3 特点和适用场景1.4 API用法1.5 代码实现 二、WebSocket2.1 什么是WebSocket2.2 工作原理2.3 特点和适用场景2.4 API用法2.5 代码实现 三、SSE与WebSocket的比较 当涉及到实现实时通信的Web应用程序时&#xff0c;两种常见的技术选…

网络安全【黑客技术】自学

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成…

每天五分钟机器学习:梯度下降算法和正规方程的比较

本文重点 梯度下降算法和正规方程是两种常用的机器学习算法,用于求解线性回归问题。它们各自有一些优点和缺点,下面将分别对它们进行详细的讨论。 区别 1. 梯度下降算法是一种迭代的优化算法,通过不断迭代调整参数来逼近最优解。它的基本思想是根据目标函数的梯度方向,沿…

openGauss学习笔记-32 openGauss 高级数据管理-批处理模式

文章目录 openGauss学习笔记-32 openGauss 高级数据管理-批处理模式32.1 语法格式32.2 参数说明32.3 示例 openGauss学习笔记-32 openGauss 高级数据管理-批处理模式 openGauss支持从文本文件执行SQL语句。openGauss提供了gsql工具实现SQL语句的批量处理。 以下场景建议使用批…