数组排序详解

news2025/3/1 3:26:05

数组排序详解

  • 1、排序算法小结
  • 2、排序算法原理及实现
    • 2.1 归并排序
    • 2.2 快速排序
    • 2.3 冒泡排序
    • 2.4 插入排序
    • 2.5 选择排序
    • 2.6 希尔排序:

1、排序算法小结

  排序算法可以分为两大类
(1)非线性时间排序算法:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。这类算法包括:冒泡排序、插入排序、选择排序、希尔排序、归并排序、快速排序、堆排序。
(2)线性时间非比较类排序算法:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。 这类算法包括:桶排序、计数排序、基数排序。不过,虽然这几种算法的时间复杂度比较低,但是他们对要排序的数据要求也比较苛刻。
  不同算法的复杂度分析如下
1

2、排序算法原理及实现

2.1 归并排序

  1. 原理
      归并排序的采用分治思想,如果要排序一个数组,我们先把数组从中间分成前后两个部分,然后对前后两个部分分别进行排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。核心是merge()合并函数。
  2. 实现
/**
 * 归并排序:
 * 采用分治思想,如果要排序一个数组,我们先把数组从中间分成前后两个部分,
 * 然后对前后两个部分分别进行排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。
 * @author mling
 */
public class mergeSortTest {
	public static void main(String[] args) {
		int[] arr = new int[]{1,3,2,5,3};
		mergeSort(arr,0,arr.length-1);
		print(arr);
	}
	/**归并算法:
	 * @param arr 数组
	 * @param l 待排序的起始下标(左边)
	 * @param r 待排序的终止下标(右边)
	 * */
	public static void mergeSort(int[] arr,int l,int r){
		if(l >= r){//递归终止条件
			return;
		}
		int mid = (l+r)/2;//取数组的中间点
		mergeSort(arr,l,mid);//对数组前半部分进行排序
		mergeSort(arr,mid+1,r);//对数组后半部分进行排序
		merge(arr,l,r,mid);//合并数组前半部分和后半部分
	}
	/**
	 * 合并数组前半部分和后半部分: 从小到大
	 * //l为起点,r为终点,mid为第1区间与第2区间的分界线
	 */
	private static void merge(int[] arr, int l, int r, int mid) {
		//初始化变量:i表示前半部分的下标,j表示后半部分的下标,k表示临时数组的下标
		int i=l,j=mid+1,k=0;
		int[] tmp = new int[r-l+1];
		while(i<=mid && j<=r){//1,2区间均未结束
			if(arr[i] <= arr[j]){//1区间的数<2区间
				tmp[k++] = arr[i++];
			}else{
				tmp[k++] = arr[j++];
			}
		}
		//将剩余数据拷贝到临时数组中
		for(;i<=mid;i++) tmp[k++]=arr[i];
		for(;j<=r;j++) tmp[k++]=arr[j];
		//将tmp临时数组拷贝回原数组
		for(int x=0;x<r-l+1;x++){
			arr[l+x]=tmp[x];
		}
	}
	private static void print(int[] arr) {
		for(int i=0; i<arr.length; i++){
			System.out.print(arr[i]);
		}
		System.out.println();
	}
}
  1. 算法分析
    (1)归并排序是一种稳定的排序算法;
    (2)最好、最坏、平均时间复杂度都是O(nlogn)。
    (3)空间复杂度是O(n),所以不是原地排序。这也是归并排序的一个致命弱点。

2.2 快速排序

  1. 原理
      快速排序也是利用分治思想。如果要排序一组数据,我们先选择这组数据中任意一个数据作为分区点pivot,然后遍历这组数据,将小于分区点pivot的放到左边,大于分区点pivot的放到右边,将pivot放到中间。然后再分别对左右两部分进行排序。核心是partition()函数。
  2. 实现
/**
 * 快速排序
 * @author rmling
 */
public class QuickSortTest {
	public static void main(String[] args) {
		int[] arr = new int[]{1,3,2,5,3};
		quickSort(arr,0,arr.length-1);
		print(arr);
	}
	/**快速排序算法:从小到大
	 * @param arr:数组   l:待排序的起始下标(左边) r:待排序的终止下标(右边)
	 * */
	public static void quickSort(int[] arr,int l,int r){
		if(l >= r){//递归终止条件
			return;
		}
		int part = partition(arr,l,r);//获取分区点
		quickSort(arr,l,part-1);//对左分区进行排序
		quickSort(arr,part+1,r);//对右分区进行排序
	}
	/**返回分区点下标*/
	private static int partition(int[] arr, int l, int r) {
		int i=l;int prv=arr[r];
		for(int j=l;j<=r;j++){
			if(arr[j] < prv){
				swap(arr,i,j);
				i++;
			}
		}
		swap(arr,i,r);
		return i;
	}
	private static void swap(int[] a,int x,int y){
		int temp=a[x];
		a[x]=a[y];
		a[y]=temp;
	}

	private static void print(int[] arr) {
		for(int i=0; i<arr.length; i++){
			System.out.print(arr[i]);
		}
		System.out.println();
	}
}
  1. 算法分析
    (1)快速排序是一种原地排序,空间复杂度为O(1)。
    (2)快速排序是一种不稳定算法。
    (3)快速排序的最好时间复杂度、平均时间复杂度为O(nlogn)。但是在极端情况下,如果数组中的数据原来就是有序的,比如1,3,5,6,8,如果我们每次选择最后一个元素作为pivot,那每次分区得到的两个区间就是不均等的,我们需要大约进行n次分区操作,才能完成快排的整个过程,每次分区我们平均要扫描大约n/2个元素,这时时间复杂度为O(n^2),也就是最坏时间复杂度。

归并与快排的区别:
(1)归并排序和快速排序都是用分治的思想,代码都是通过递归来实现的,但是归并排序的核心是merge()合并函数,而快速排序的核心是partition()分区函数。归并排序的处理过程是从下到上,先处理子问题,然后再合并。而快速排序的处理过程正好相反,它的处理过程是从上到下的,先分区,然后再处理子问题。
(2)归并排序算法是一种任何情况下时间复杂度都比较稳定的排序算法,时间复杂度都是O(nlogn);快速排序算法最坏情况下时间复杂度为O(n^2) ,但是平均时间复杂度都是O(nlogn),不仅如此,快速排序时间复杂度退化到O(n^2)的概率非常小,我们可以通过合理选择pivot来避免这种情况。
(3)归并排序不是原地排序算法,空间复杂度比较高,为O(n);快速排序是原地排序算法,空间复杂度为O(1)。
(4)归并排序是稳定的算法,快速排序不稳定。

2.3 冒泡排序

  1. 原理
      冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系的要求。如果不满足就让它俩互换位置。一次冒泡会让至少一个元素移动到它应该在的位置,重复n次,就完成了n个数据的排序工作。
  2. 算法描述
    (1)比较相邻的元素。如果第一个比第二个大,就交换它们两个;
    (2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
    (3)针对所有的元素重复以上的步骤,除了最后一个;
    重复步骤1~3,直到排序完成。
  3. 实现
/**
 * 冒泡排序
 * @author mling
 */
public class BubbleSortTest {
	public static void main(String[] args) {
		int[] arr = new int[]{1,3,2,5,3};
		bubbleSort(arr);
		print(arr);
	}
	/**从小到大排序 */
	public static void bubbleSort(int[] arr){
		int len = arr.length;
		for(int i=0; i<len; i++){
			boolean flag = false;//提前退出冒泡循环的标志
			for(int j=0;j<len-i-1;++j){
				if(arr[j] > arr[j+1]){
					swap(arr,j,j+1);
					flag=true;//true表示有数据交换
				}
			}
			if(!flag) break;//没有数据交换,提前退出
		}
	}
	private static void swap(int[] a,int x,int y){
		int temp=a[x];a[x]=a[y];a[y]=temp;
	}
	private static void print(int[] arr) {
		for(int i=0; i<arr.length; i++){	System.out.print(arr[i]);}
		System.out.println();
	}
}
  1. 算法分析
    (1)冒泡的过程中只涉及相邻数据的交换操作,只需要常量级的临时空间,所以它的空间复杂度为O(1),是一个原地排序算法。
    (2)冒泡排序是稳定的排序算法。
    (3)最好时间复杂度为O(n),最坏时间复杂度为O(n*n),时间复杂度为O(n*n).

2.4 插入排序

  1. 原理
      将数组的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。算法的核心思想就是,取未排序区间中的元素,在已排序区间中找到合适的位置将其插入,并保证已排序区间的数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
  2. 算法描述
    (1)从第一个元素开始,该元素可以认为已经被排序;
    (2)取出下一个元素,在已经排序的元素序列中从后向前扫描;
    (3)如果该元素(已排序)大于新元素,将该元素移到下一位置;
    (4)重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
    (5)将新元素插入到该位置后;
    (6)重复步骤2~5。
  3. 实现
/**
 * 插入排序
 * @author mling
 */
public class InsertSortTest {
	public static void main(String[] args) {
		int[] arr = new int[]{1,3,2,5,3};
		insertSort(arr);
		print(arr);
	}
	/**从小到大排序 */
	public static void insertSort(int[] arr){
		int len = arr.length;
		for(int i=1; i<len; i++){
			int current = arr[i];
			//查找需要插入的位置
			int j=i-1;
			for(;j>=0;j--){
				if(arr[j] > current){
					arr[j+1]=arr[j];
				}else{
					break;
				}
			}
			arr[j+1] = current;
		}
	}
	private static void print(int[] arr) {
		for(int i=0; i<arr.length; i++){
			System.out.print(arr[i]);
		}
		System.out.println();
	}
}
  1. 算法分析
    (1)插入算法的运行只需要用到一个额外的额存储空间,所以空间复杂度为O(1),是原地排序算法。
    (2)插入算法是稳定的排序算法。
    (3)最好时间复杂度为O(n),最坏时间复杂度为O(n*n),平均时间复杂度为O(n^2)。

2.5 选择排序

  1. 算法原理
      选择排序的实现思路和插入排序类似,也分为已排序区间和未排序区间。但是选择排序每次会从未排序区间中选择最小(最大)的元素,存放已排序区间的末尾。重复此操作,直到所有元素排序完毕。
  2. 算法描述
    (1)初始状态:无序区为R[1…n],有序区为空;
    (2)第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
    (3)n-1趟结束,数组有序化了。
  3. 算法实现
/**
 * 插入排序
 * @author mling
 */
public class SelectSortTest {
	public static void main(String[] args) {
		int[] arr = new int[]{1,3,2,5,3};
		selectionSort(arr);
		print(arr);
	}
	/**选择排序:从小到大*/
	public static void selectionSort(int[] arr){
		int len = arr.length;
		int minIndex;
		for(int i=0;i<len;i++){
			minIndex=i;
			//寻找未排序部分的最小值的索引
			for(int j=i+1;j<len;j++){
				if(arr[j]<arr[minIndex]){
					minIndex=j;
				}
			}
			swap(arr,i,minIndex);
		}
	}
	private static void swap(int[] a,int x,int y){
		int temp=a[x];a[x]=a[y];a[y]=temp;
	}
	private static void print(int[] arr) {
		for(int i=0; i<arr.length; i++){
			System.out.print(arr[i]);
		}
		System.out.println();
	}
}
  1. 算法分析
    (1)选择排序的空间复杂度为O(1),是一种原地排序算法。
    (2)选择排序不是一种稳定的排序算法。因为选择排序每次都要从未排序区间中选择最小值,并和前面的元素交换位置,这样会破坏稳定性。
    (3)选择排序的最好、最坏、平均时间复杂度都是O(n^2)。

2.6 希尔排序:

  1. 算法原理:
      希尔排序是简单插入排序的改进版。他与插入排序的不同之处在于,它会优先比较较远的元素。希尔排序又叫缩小增量排序。希尔排序的核心在于间隔序列的设定(也就是增量)。既可以提前设定好间隔序列,也可以动态定义间隔序列。
  2. 算法描述
    先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
    (1)选择一个增量序列t1,t2,…,tk,其中ti>tj,i<j,tk=1;
    (2)按增量序列个数k,对序列进行k 趟排序;
    (3)每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m(m=length/ti) 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
  3. 算法实现:
/**
 * 希尔排序
 * @author mling
 */
public class ShellSortTest {
	public static void main(String[] args) {
		int[] arr = new int[]{1,3,2,5,3};
		shellSort(arr);
		print(arr);
	}
	/**希尔排序:从小到大*/
	public static void shellSort(int[] arr){
		int len = arr.length;
		int gap=1;//希尔排序的关键在于设置增量,这里我们动态设置一个增量
		while(gap<len/3){
			gap=gap*3+1;
		}
		int temp;
		for(;gap>0;gap=(int) Math.floor(gap/3)){
			for(int i=gap;i<len;i++){
				temp = arr[i];
				int j=i-gap;
				for(;j>=0 && arr[j]>temp;j-=gap){
					arr[j+gap]=arr[j];
				}
				arr[j+gap]=temp;
			}
		}
	}
	private static void print(int[] arr) {
		for(int i=0; i<arr.length; i++){
			System.out.print(arr[i]);
		}
		System.out.println();
	}
}
  1. 算法分析:
    (1)希尔排序的空间复杂度为O(1),是一种原地排序算法。
    (2)希尔排序不是一种不稳定的排序算法。多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以希尔排序是不稳定的。
    (3)选择排序的最好时间复杂度为O(n)、最坏时间复杂度都是O(n*n),平均时间复杂度为O(n^1.3)。

参考链接: 排序算法

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

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

相关文章

用Python实现批量翻译文档文件

文件名批量翻译需要用到编程语言和相应的翻译 API&#xff0c;下面以 Python 和 Google 翻译 API 为例&#xff0c;介绍具体的实现步骤&#xff1a; 安装必要的 Python 库 使用 Python 代码进行文件名翻译需要先安装两个库&#xff1a;googletrans 和 os。 pip install googl…

全网最全的Cookie, Session, Token鉴权详解,一定让你大饱眼福

目录 前言 一、Cookie鉴权 二、Session鉴权 ​编辑 三、Token鉴权 四、总结 前言 在Web开发中&#xff0c;鉴权是保护用户数据和系统安全的重要手段之一。常见的鉴权方式包括Cookie、Session和Token三种&#xff0c;下面我将详细介绍这三种鉴权方式&#xff0c;并且探讨它…

FMC子卡设计资料:ADRV9002双窄带宽带射频收发器 AD9361双收双发射频 DA子卡 ADRV9009双收双发射频FMC子卡

FMC550-基于ADRV9002双窄带宽带射频收发器FMC子卡 一、产品概述 ADRV9002 是一款高性能、高线性度、高动态范围收发器&#xff0c;旨在针对性能与功耗系统进行优化。该设备是可配置的&#xff0c;非常适合要求苛刻、低功耗、便携式和电池供电的设备。ADRV9002 的工作频率为…

基于典型相关分析的故障检测和过程监控算法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。⛳座右铭&#…

分布式消息中间件概念

对于分布式消息中间件&#xff0c;首先要了解两个基础的概念&#xff0c;即什么是分布式系统&#xff0c;什么又是中间件。 分布式系统 “A distributed system is one in which components located at networked computers communicate and coordinate their actions only by …

Java 中常用的版本对应关系,springboot springcloud,cloud-alibaba

1.网址链接&官方文档链接 SpringCloud与SpringBoot的版本对应关系:https://start.spring.io/actuator/info SpringBoot版本列表&#xff1a;https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent SpringBoot最新GA版官方文档&#…

【计算机网络——制作双绞线】

文章目录实验目的:实验内容及过程记录&#xff08;一&#xff09;剪线&#xff08;二&#xff09;剥皮&#xff08;三&#xff09;排序&#xff08;四&#xff09;剪齐&#xff08;五&#xff09;插入&#xff08;六&#xff09;压制&#xff1a;&#xff08;七&#xff09;检测…

vscode debug Attach mode

Python Debug Mode: 常规debug。针对*.py python脚本启动的场景。 在使用vscode debug时&#xff0c;如果调试python脚本其实很简单。 .vscode/launch.json文件。 {"version": "0.2.0","configurations": [{"name": "Python: C…

7年经验,功能测试工程师该如何一步步提升自己的能力呢?

功能测试&#xff08;所谓“点点点”&#xff09;在行业中基本能拿到10k左右的薪水&#xff0c;但是你不可能一直点。入行3年后&#xff0c;你需要拥有不止点点点的技能&#xff0c;否则出去面试&#xff0c;你会就会感受到竞争者给你带来的压力&#xff0c;你需要拿出更高级的…

真题详解(Flynn分类)-软件设计(四十六)

真题详解&#xff08;计算机总线&#xff09;-软件设计&#xff08;四十五)https://blog.csdn.net/ke1ying/article/details/130046829 Flynn分类将计算机分为四类。 单指令流单数据流机器&#xff08;SISD&#xff09;&#xff1a;早期的机器&#xff0c;在某个时钟周期&…

C++中的输入输出流iostream、文件流fstream、字符串流sstream解释

文章目录前言流的理解流的优势C中流的分类IO流字符串流sstream基本概念使用文件流fstream文本文件写文件读文件前言 流的解释。 流的理解 流的本质是一种对象。 流是介于数据和程序之间的一个中转设备。 因为流的存在&#xff0c;使得我们可以不需要直接操作数据&#xff…

瑞吉外卖Linux

1、linux简介 Linux系统版本 Linux系统分为内核版和发行版 内核版&#xff1a; 由LinusTorvalds及其团队开发、维护 免费、开源 负责控制硬件 发行版&#xff1a; 基于Linux内核版进行扩展 由各个Linux厂商开发、维护 有收费…

自动插入匹配与标题相关的百度图片或者搜狗图片软件-批量插入txt文档-Chatgpt批量写文章配图神器

1、我们用《Chatgpt 3.5-turbo软件》批量生成txt文档&#xff0c;但是这样txt文档里不带图片&#xff0c;直接发布到网站上&#xff0c;光有文字没有图片&#xff0c;效果也不是很理想&#xff0c;就需要一款配图软件。 2、该软件根据txt标题自动匹配百度图片或者搜狗图片里的…

Redis高可用

最近离职后还没开始找工作&#xff0c;在找工作前去学习一下Redis高可用方案。 目录Redis高可用高可用的概念实现方式持久化主从复制简单结构优化结构优缺点哨兵模式&#xff08;Sentinel&#xff09;哨兵进程的作用自动故障迁移(Automatic failover)优缺点集群优缺点Redis高可…

300左右买什么蓝牙耳机好?300元内音质最好的蓝牙耳机

在市场上有很多蓝牙耳机。当时无线蓝牙耳机如此流行也是由苹果公司提出。作为首次取消手机上3.5毫米耳机插孔的手机&#xff0c;后面许多手机也纷纷效仿。但这也促进了科学技术的发展。下面分享几款300元内音质表现不错的蓝牙耳机。 第一款&#xff1a;南卡小音舱Lite2蓝牙耳机…

【深度思考】人工智能技术对未来各行业的影响,我们该如何应对?

目录 1、AI的应用对行业带来的机遇 2、AI将冲击哪些行业 3、如何应对AI对个人发展的挑战 4、AI对于程序员的冲击 随着技术的不断发展&#xff0c;AI已经开始深刻影响着各行各业。从生产制造到金融、医疗和交通等行业&#xff0c;人工智能技术都在发挥着越来越重要的作用。现…

ijkplayer编译支持x264编码器

ijkplayer作为一款基于ffmpeg在移动端比较流行的播放器&#xff0c;并没有集成编码器。如果使用ffmpeg且需要音视频编码功能的话就需要自己手动集成。 编译支持x264编码器的流程主要参考了 ijkplayer支持x264编译 - 简书 原理与ijkplayer编译支持openssl的流程类似。 1 下载x…

项目管理 | 10年项目经理推荐的一份书单:你认真读过几本?

作为一名项目经理&#xff0c;我们需要在团队之间协调合作&#xff0c;管理预算和资源&#xff0c;确保项目按时、按预算顺利完成。因此&#xff0c;学习项目管理知识是提高我们的职业素养、职业技能和职业竞争力的关键。 今天就来给大家分享几本具有广泛影响力和权威性的项目…

如何动态的给一个类增加功能?

装饰器模式概述 装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为&#xff0c;在现实生活中&#xff0c;这种情况也到处存在&#xff0c;例如一张照片&#xff0c;我们可以不改变照片本身&#xff0c;给它增加一个相框&#xff0c;使得它具有防潮的功能&a…

跨域问题?别担心!Spring Boot的5种奇巧淫技解救你

来源&#xff1a;https://blog.csdn.net/weter_drop/article/details/112135940 一、为什么会出现跨域问题 出于浏览器的同源策略限制。同源策略&#xff08;Sameoriginpolicy&#xff09;是一种约定&#xff0c;它是浏览器最核心也最基本的安全功能&#xff0c;如果缺少了同…