八大排序算法 - Java实现

news2024/11/24 6:19:20

冒泡排序

排序原理:

  1. 比较相邻的元素。如果前一个元素比后一个元素大,就交换这两个元素的位置。
  2. 对每一对相邻元素做同样的工作,从开始第一对元素到结尾的最后一对元素。最终最后位置的元素就是最大值

 代码实现:

import java.util.Arrays;

public class BubbleSort {
	public static void main(String[] aaa) {
		int[] a=new int[]{5,2,4,3,1,8,6,7};
		sort(a);
		System.out.print(Arrays.toString(a));
	}
	
	public static void sort(int[] a) {
		for(int i=0;i<a.length-1;i++)
		{			
			for(int j=0;j<a.length-1-i;j++)
			{
				if(a[j]>a[j+1])
				{
					int temp=a[j];
					a[j]=a[j+1];
					a[j+1]=temp;
				}
			}
		}
	}
}

时间复杂度分析:

        冒泡排序使用了双层for循环,其中内层循环的循环体是真正完成排序的代码,所以,

我们分析冒泡排序的时间复杂度,主要分析一下内层循环体的执行次数即可。

在最坏情况下,也就是假如要排序的元素为{6,5,4,3,2,1}逆序,那么:

  • 元素比较的次数为: (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;
  • 元素交换的次数为: (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

总执行次数为:(N^2/2-N/2)+(N^2/2-N/2)=N^2-N;

按照大O推导法则,保留函数中的最高阶项那么最终冒泡排序的时间复杂度为O(N^2)

选择排序

排序原理:

  1. 每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,如果当前索引处的值大于其他某个索引处的值,则假定其他某个索引出的值为最小值,最后可以找到最小值所在的索引
  2. 交换第一个索引处和最小值所在的索引处的值

代码实现:

import java.util.Arrays;

public class SearchSort {
	public static void main(String[] aaa) {
		int[] a=new int[]{5,2,4,3,1,8,6,7};
		sort(a);
		System.out.print(Arrays.toString(a));
	}
	public static void sort(int[] a) {
		//第一层循环,控制遍历次数
		for(int i=0;i<a.length-1;i++)
		{
			//min记录最小的数
			int min=a[i];
			//minNum记录最小的数的下标,用于下面的交换
			int minNum=i;
			//第二层循环,遍历比较出最小值
			for(int j=i+1;j<a.length;j++)
			{
				if(min>a[j])
				{
					min=a[j];
					minNum=j;
				}
			}
			//将最小值移动到最前面
			a[minNum]=a[i];
			a[i]=min;
		}
	}
}

时间复杂度分析:

选择排序使用了双层for循环,其中外层循环完成了数据交换,内层循环完成了数据比较,所以我们分别统计数据交换次数和数据比较次数:

  • 数据比较次数:(N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;
  • 数据交换次数:N-1

总执行次数为:N^2/2-N/2+(N-1)=N^2/2+N/2-1;

根据大O推导法则,保留最高阶项,去除常数因子,时间复杂度为O(N^2)

插入排序

排序原理:

  1. 把所有的元素分为两组,已经排序的和未排序的;
  2. 找到未排序的组中的第一个元素,向已经排序的组中进行插入;
  3. 倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待插入元素放到这个位置,其他的元素向后移动一位;

代码实现:

import java.util.Arrays;

public class InsertSort {

	public static void main(String[] aaa) {
		int[] a=new int[]{1,3,12,5,3,123,5,4676,54};
		System.out.println(Arrays.toString(a));
		sort(a);
		System.out.println(Arrays.toString(a));
	}
	
	public static void sort(int[] a)
	{
		for(int i=1;i<a.length;i++)
		{
			for(int j=i;j-1>=0;j--)
			{
				if(a[j-1]>a[j])
				{
					int temp=a[j-1];
					a[j-1]=a[j];
					a[j]=temp;
				}
				else break;
			}
		}
	}

}

 时间复杂度分析:

插入排序使用了双层for循环,其中内层循环的循环体是真正完成排序的代码,所以,我们分析插入排序的时间复杂度,主要分析一下内层循环体的执行次数即可。

最坏情况,也就是待排序的数组元素为{12,10,6,5,4,3,2,1},那么:

  • 比较的次数为: (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;
  • 交换的次数为: (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

总执行次数为:(N^2/2-N/2)+(N^2/2-N/2)=N^2-N;

按照大O推导法则,保留函数中的最高阶项那么最终插入排序的时间复杂度为O(N^2)

快速排序

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

排序原理:

  1. 首先设定一个分界值,通过该分界值将数组分成左右两部分。
  2. 将大于或等于分界值的数据放到到数组右边,小于分界值的数据放到数组的左边。此时左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
  3. 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
  4. 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左侧和右侧两个部分的数据排完序后,整个数组的排序也就完成了。

切分原理:

     把一个数组切分成两个子数组的基本思想:

  1. 找一个基准值,用两个指针分别指向数组的头部和尾部;
  2. 先从尾部向头部开始搜索一个比基准值小的元素,搜索到即停止,并记录指针的位置;
  3. 再从头部向尾部开始搜索一个比基准值大的元素,搜索到即停止,并记录指针的位置;
  4. 交换当前左边指针位置和右边指针位置的元素;
  5. 重复2,3,4步骤,直到左边指针的值大于右边指针的值停止;

 代码实现:

import java.util.Arrays;

public class QuickSort {
	public static void main(String[] args) {
		int[] a=new int[]{50,23,88,35,65,53,90,13,52,42,16,18,73,25,77,66};
		System.out.println(Arrays.toString(a));
		sort(a,0,a.length-1);
		System.out.println(Arrays.toString(a));
	}
	
	public static void sort(int[] a,int b,int e)
	{
		if(b>=e) return;
		int i=b;
		int j=e;
		while(i<j) {
			if(a[i]>a[i+1]) {
				int temp=a[i];
				a[i]=a[i+1];
				a[i+1]=temp;
				i++;
			}else {
				int temp=a[j];
				a[j]=a[i+1];
				a[i+1]=temp;
				j--;
			}
			
		}
		sort(a,b,i-1);
		sort(a,i+1,e);
	}
		
}

时间复杂度分析:

快速排序的一次切分从两头开始交替搜索,直到left和right重合,因此,一次切分算法的时间复杂度为O(n),但整个快速排序的时间复杂度和切分的次数相关。

最优情况:每一次切分选择的基准数字刚好将当前序列等分。

 如果我们把数组的切分看做是一个树,那么上图就是它的最优情况的图示,共切分了logn次,所以,最优情况下快速排序的时间复杂度为O(nlogn);

最坏情况:每一次切分选择的基准数字是当前序列中最大数或者最小数,这使得每次切分都会有一个子组,那么总共就得切分n次,所以,最坏情况下,快速排序的时间复杂度为O(n^2);

平均情况:每一次切分选择的基准数字不是最大值和最小值,也不是中值,这种情况我们可以用数学归纳法证明,快速排序的时间复杂度为O(nlogn);

希尔排序

排序原理:

  1. 选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组;
  2. 对分好组的每一组数据完成插入排序;
  3. 减小增长量,最小减为1,重复第二步操作;

代码实现:

import java.util.Arrays;

public class ShellSort {
	public static void main(String[] args) {
		int[] a=new int[]{5,2,4,35,13,1,8,6,32,7,543};
		System.out.println(Arrays.toString(a));
		sort(a);
		System.out.println(Arrays.toString(a));
	}
	
	public static void sort(int[] a)
	{
		for(int gap=a.length/2;gap>0;gap/=2)
		{
			for(int i=gap;i<a.length;i++)
			{
				for(int j=i;j-gap>=0;j--)
				{
					if(a[j-gap]>a[j])
					{
						int temp=a[j-gap];
						a[j-gap]=a[j];
						a[j]=temp;
					}
					else break;
				}
			}
		}
	}
}

时间复杂度分析:

在希尔排序中,增长量h并没有固定的规则,有很多论文研究了各种不同的递增序列,但都无法证明某个序列是最好的,因此对于希尔排序不做时间复杂度分析

归并排序

排序原理:

  1. 尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是1为止;
  2. 将相邻的两个子组进行合并成一个有序的大组;
  3. 不断的重复步骤2,直到最终只有一个组为止;

 归并原理:

代码实现:

import java.util.Arrays;

public class MergeSort1 {
	public static void main(String[] args) {
		int[] a=new int[]{50,23,88,35,65,53,90,13,52,42,16,18,73,25,77,66};
		System.out.println(Arrays.toString(a));
		sort(a,0,a.length-1);
		System.out.println(Arrays.toString(a));
	}
	
	public static void sort(int[] a,int l,int r)
	{
		if(l==r) return;
		
		int mid=(l+r)/2;
		sort(a,l,mid);
		sort(a,mid+1,r);
		
		int[] temp=new int[r-l+1];
		int i=0;
		int s1=l;
		int s2=mid+1;
		while(s1<=mid && s2<=r)
		{
			if(a[s1]<a[s2])
			{
				temp[i]=a[s1];
				s1++;
				i++;
			}else {
				temp[i]=a[s2];
				s2++;
				i++;
			}
		}
		while(s1<=mid)
		{
			temp[i]=a[s1];
			s1++;
			i++;
		}
		while(s2<=r)
		{
			temp[i]=a[s2];
			s2++;
			i++;
		}
		for(int i1=0;i1<temp.length;i1++)
		{
			a[l+i1]=temp[i1];
		}
		
	}
}

时间复杂度分析:

用树状图来描述归并,如果一个数组有8个元素,那么它将每次除以2找最小的子数组,共拆log8次,值为3,所以树共有3层,那么自顶向下第k层有2^k个子数组,每个数组的长度为2^(3-k),归并最多需要2^(3-k)次比较。因此每层的比较次数为 2^k * 2^(3-k)=2^3,那么3层总共为 3*2^3。

假设元素的个数为n,那么使用归并排序拆分的次数为log2(n),所以共log2(n)层,那么使用log2(n)替换上面3*2^3中的3这个层数,最终得出的归并排序的时间复杂度为:log2(n)*2^(log2(n))=log2(n)*n,根据大O推导法则,忽略底数,最终归并排序的时间复杂度为O(nlogn);

堆排序

 堆可以分为大根堆,小根堆

大根堆:每个节点的值都大于或者等于他的左右孩子节点的值

小根堆:每个结点的值都小于或等于其左孩子和右孩子结点的值0

堆是用数组完成数据元素的存储的,由于数组的底层是一串连续的内存地址,所以我们要往堆中插入数据,我们只能往数组中从索引0处开始,依次往后存放数据,但是堆中对元素的顺序是有要求的,每一个结点的数据要大于等于它的两个子结点的数据,所以每次插入一个元素,都会使得堆中的数据顺序变乱,这个时候我们就需要通过一些方法让刚才插入的这个数据放入到合适的位置。

以大根堆的调整为例:

同样,删除元素也需要进行堆调整:

对于大根堆,索引1处的元素,也就是根结点是最大的元素,当我们把根结点的元素删除后,需要有一个新的根结点出现,这时我们可以暂时把堆中最后一个元素放到索引1处,充当根结点,但是它不满足大根堆的有序性需求,这个时候我们就需要通过一些方法,让这个新的根结点放入到合适的位置。

堆排序实现步骤(使用大根堆,实现从小到大排序):

  1. 构造大根堆;
  2. 得到堆顶元素,这个值就是最大值;
  3. 交换堆顶元素和数组中的最后一个元素,此时所有元素中的最大元素已经放到合适的位置;
  4. 对堆进行调整,重新让除了最后一个元素的剩余元素中的最大值放到堆顶;
  5. 重复2~4这个步骤,直到堆中剩一个元素为止;
public class HeapSort {
	public static void main(String[] args) {
		int[] a=new int[]{5,32,4,23,1,8,64,67,35,85,12,123,543,45,23,10};
		System.out.println(Arrays.toString(a));
		int l=a.length;
		for(int i=0;i<a.length-1;i++)
		{
			for(int p=l-1;p>=0;p--)
			{
				sort(a,p,l);
			}
			int temp=a[0];
			a[0]=a[l-1];
			a[l-1]=temp;
			l--;
		}
		System.out.println(Arrays.toString(a));
	}
	
	public static void sort(int[] a,int p,int l)
	{
		int c=2*p+1;
		if(c<l)
		{
			int rc=c+1;
			if(rc<l && a[c]<a[rc]) c=rc;
			if(a[p]<a[c])
			{
				int temp=a[p];
				a[p]=a[c];
				a[c]=temp;
				p=c;
				sort(a,p,l);
			}
 		}
	}
	
}

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

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

相关文章

图神经网络学习

入门 目的&#xff1a;训练一个图模型&#xff0c;使得该图模型可以区分图上的黄色节点和绿色节点。 特征作为图的节点&#xff0c;颜色就是图的分类。 图的度的概念&#xff1a;与节点相连的条数。 邻接表记录的是后续邻居的信息&#xff1b; 在新闻推荐中&#xff0c;节点是…

2021年09月 C/C++(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;余数相同问题 已知三个正整数 a&#xff0c;b&#xff0c;c。 现有一个大于1的整数x&#xff0c;将其作为除数分别除a&#xff0c;b&#xff0c;c&#xff0c;得到的余数相同。 请问满足上述条件的x的最小值是多少? 数据保证x有解。 输入 一行&#xff0c;三…

微服务中间件--Nacos

Nacos 1. Nacos入门a.服务注册到Nacosb.Nacos服务分级存储模型c.NacosRule负载均衡d.服务实例的权重设置e.环境隔离 - namespacef.Nacos和Eureka的对比 2. Nacos配置管理a.统一配置管理b.配置热更新c.多环境配置共享 1. Nacos入门 Nacos是阿里巴巴的产品&#xff0c;现在是Spr…

【前端|JS实战第1篇】使用JS来实现属于自己的贪吃蛇游戏!

前言 贪吃蛇游戏是经典的小游戏&#xff0c;也是学习前端JS的一个很好的练习项目。在本教程中&#xff0c;我们将使用 JavaScript 来逐步构建一个贪吃蛇游戏。我们会从创建游戏区域开始&#xff0c;逐步添加蛇的移动、食物的生成以及游戏逻辑等功能。 &#x1f680; 作者简介&a…

tcl学习之路(五)(Vivado时序约束)

1.主时钟约束 主时钟通常是FPGA器件外部的板机时钟或FPGA的高速收发器输出数据的同步恢复时钟信号等。下面这句语法大家一定不会陌生。该语句用于对主时钟的名称、周期、占空比以及对应物理引脚进行约束。 create_clock -name <clock_name> -periood <period> -wa…

电影《孤注一掷》感触、计算机底层二进制与十进制的转换

今天&#xff0c;我与媳妇一同在商场吃完午餐&#xff0c;正值天空绵绵细雨。近期&#xff0c;听闻一部名为《孤注一掷》的电影&#xff0c;其主人公是一位程序员&#xff0c;故事情节围绕境外电信诈骗展开&#xff0c;引发了广泛的关注。身为一名程序员&#xff0c;我对与电信…

DSO 系列文章(2)——DSO点帧管理策略

文章目录 1.点所构成的残差Residual的管理1.1.前端残差的状态1.2.后端点的残差的状态1.3.点的某个残差的删除 2.点Point的管理2.1.如何删除点——点Point的删除2.2.边缘化时删除哪些点&#xff1f; 3.帧FrameHessian的管理 DSO代码注释&#xff1a;https://github.com/Cc19245/…

Vulnhub: DriftingBlues: 2靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.207 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.207 80端口的/blog目录为wordpress wpscan收集wordpress用户和爆破密码 wpscan --url http://driftingblues.box/blog -e…

深入理解Linux内核--回收页框

页框回收算法 Linux中有一点很有意思&#xff0c;在为用户态进程与内核分配动态内存时&#xff0c;所作的检查是马马虎虎的。 比如&#xff0c;对单个用户所创建进程的RAM使用总量并不作严格检查(第三章的“进程资源限制”一节提到的限制只针对单个进程); 对内核使用的许多磁盘…

【数据结构】链表的回文结构

文章目录 &#x1f30f;引言&#x1f9ed;[链表的回文结构](https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId49&&tqId29370&rp1&ru/activity/oj&qru/ta/2016test/question-ranking)&#x1f6a9;&#x1f6a9;题目描述&#xf…

学习网络编程No.3【socket理论实战】

引言&#xff1a; 北京时间&#xff1a;2023/8/12/15:32&#xff0c;自前天晚上更新完文章&#xff0c;看了一下鹅厂新出的《扫毒3》摆烂至现在&#xff0c;不知道是长大了&#xff0c;还是近年港片就那样&#xff0c;给我的感觉不是很好&#xff0c;也可能是国内市场对港片不…

XXL-JOB任务调度中心后台命令执行漏洞

漏洞描述 XXL-JOB任务调度中心后台存在命令执行漏洞,攻击者可在后台通过写入shell命令任务调度获取服务器控制权限 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权…

AndroidAGP8.1.0和JDK 17迁移之旅

AndroidAGP8.1.0和JDK 17迁移之旅 前言&#xff1a; 由于我最近写demo的直接把之前的项目从AGP4.2.2升级到8.1.0引发了一些列问题&#xff0c;这里记录一下&#xff0c;前面讲解过迁移DSL方式遇到的问题&#xff0c;这次升级8.1.0也比之前顺利多了&#xff0c;想看DSL迁移的可…

Edge浏览器免费使用GPT3.5

搜索sider,安装Sidebar插件 注册账号即可每天免费使用30次。 Sider: ChatGPT侧边栏,GPT-4, 联网, 绘图

Excel VBA 复制除指定工作表外所有的工作表的内容到一张工作表中

当我们有一张表里面有很多sheet 具有相同的表结构&#xff0c;如果需要汇总到一张表中&#xff0c;那么我们可以借助VBA 去实现汇总自动化 Sub 复制所有工作表内容()Dim ws As WorksheetDim targetSheet As WorksheetDim lastRow As Long 设置目标表格&#xff0c;即要将所有…

SpringMVC之@RequestMapping注解

文章目录 前言一、RequestMapping介绍二、详解&#xff08;末尾附源码&#xff0c;自行测试&#xff09;1.RequestMapping注解的位置2.RequestMapping注解的value属性3.RequestMapping注解的method属性4.RequestMapping注解的params属性&#xff08;了解&#xff09;5.RequestM…

课程项目设计--项目建立--宿舍管理系统--springboot后端

前要 项目设计–宿舍管理系统 文章目录 项目建立导入依赖配置文件配置目录结构config配置mybatis-plusswagger 生成实体、mapper和servicebaseEntity统一响应实例响应码接口响应码接口实现统一响应result统一分页响应 项目建立 太长了&#xff0c;修改一下 导入依赖 暂时先加…

Java 项目日志实例:Log4j2

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ Apache Log4j 2 是对 Log4j 的升级&#xff0c;与其前身 Log4j 1.x 相比有了显着的改进&#xff0c;并提供了许多 Logback 可用的改进&#xff0c;同时支持 JCL 以及 SLF4J…

Word中对象方法(Methods)的理解及示例(上)

【分享成果&#xff0c;随喜正能量】奋斗没有终点,任何时候都是一个起点&#xff0c;沉潜是为了蓄势待发&#xff0c;沉潜是为了等待因缘。鲸豚沉潜于大海&#xff0c;幽兰深藏于山谷&#xff0c;能够经得起沉潜的人&#xff0c;才会有更高的成就。正如一年的树木只能当柴烧&am…

C++入门:函数缺省参数与函数重载

目录 1.函数缺省参数 1.1 缺省参数概念 1.2 缺省参数分类 2.函数重载 2.1 函数重载概念 2.2 C支持函数重载的原理 1.函数缺省参数 1.1 缺省参数概念 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时&#xff0c;如果没有指定实 参则采用该形参的…