Java排序算法 - 交换算法之 冒泡和快排

本文详细介绍了两种经典的排序算法——冒泡排序和快速排序。冒泡排序是稳定的内排序算法,适合小规模数据,而快速排序是效率较高的非稳定排序算法,适用于大规模数据。文中通过实例解析了冒泡排序和快速排序的原理,并提供了两种快速排序的实现方式:挖坑填数和交换写法。此外,还讨论了它们各自的时间复杂度和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、交换排序简介

交换排序包含冒泡排序和快速排序,冒泡排序是一种非常简单的稳定的排序算法,快速排序是一种较为复杂非稳定的排序算法。
冒泡排序的核心思想是,遍历数组,重复扫描待排序序列,并比较每一对相邻的元素,当该对元素顺序不正确时进行交换。一直重复这个过程,直到没有任何两个相邻元素可以交换,就表明完成了排序。快速排序是对冒泡排序的优化,核心思想是分而治之,利用一个中枢值,把比中枢值大的数字排在右边,比中枢值小的数字排在左边,这样就完成一趟快排,然后将中枢值左边和右边的数分别再重复以上过程,直到每趟快排的数只有一个数字,就完成了排序。

平均时间复杂度最好最坏空间复杂度排序方式稳定性
冒泡排序O(n²)O(n)O(n²)O(n)内排序稳定
快速排序O(nlogn)O(nlogn)O(n²)O(logn)内排序稳定

二、应用场景

冒泡适用于小数量级的场景,快排适用于数据量较大的场景,数据量特别大则更适合用堆排序。

三、代码实现

冒泡写法有很多种,这里只写最简单最容易记的。快速排序分递归和非递归写法,这里先只写递归写法的挖矿填数、交换写法。

冒泡排序
public static void maopaoSort(int[] nums){
	for(int i=0; i<nums.length; i++){
		for(int j = i+1; j<nums.length; j++){
			if(nums[j] < nums[i]){
				int temp = nums[i];
				nums[i] = nums[j];
				nums[j] = temp;
			}
		}
	}
}

快速排序,只有三个部分,第一个部分是中枢值,然后是左右数组区间。
无论什么写法,做的事情都是一样的。每趟排序先确定一个中枢值,然后把比中枢值小的数放到数组左边区间,比中枢值大的数放到右边区间,这样中枢值的左边都是比它小的数字,右边都是比它大的数字。
举例:[4,1,5,6,2] 使用挖坑填数写法
第一趟,中枢值为4,本次排序数组区间是 0到4,左指针 i 起始为0 右指针 j 为4。
while外层第一次循环 i = 0 j = 4
1、从右找,j=4的数字2比中枢值小,覆盖到索引i位置,[2,1,5,6,2] i++ i = 1 j = 4
2、从左找,i=2的数字5比中枢值大,覆盖到索引j位置,[2,1,5,6,5] j-- i = 2 j = 3
while外层第二次循环 i = 2 j = 3
3、从右找,j=2的数字1比中枢值小,但 此时 j=1 i = 2 ,不覆盖,本次循环后续代码也不执行
4、i 比 j 大,跳出外层while循环
5、中枢值归位,[2,1,4,6,5] i = 2
6、执行递归,以中枢值位置左右区分区间,分别再进行排序。[2,1] 左区间、[6,5]右区间再进行快排。
第二趟,[2,1] 中枢值为2
while外层第一次循环 i = 0 j = 1
1、从右边找比中枢值小的数字1, 覆盖i [1,1] i++ i = 1, j = 1
2、从左边找比中枢值大的数字,i == j 不执行后续代码,结束本次循环
3、中枢值归位, i = 1 [1,2]
4、执行递归,因中枢值是2,左边区间是[1], 右边[]
第三趟, [6,5] 中枢值为6
while外层第一次循环 i = 0 j = 1
1、从右边找比中枢值小的数字5, 覆盖i[5,5] i++ i = 1, j = 1
2、从左边找比中枢值大的数字,i == j 不执行后续代码,结束本次循环
3、中枢值归位,i = 1 [5,6]
4、执行递归,因中枢值是6,左边区间是[5],右边[]
第四趟, [1]
1、start = 0, end = 0 不执行排序,跳出递归
第五趟,[]
1、start = 2, end = 1 不执行排序,跳出递归
第六趟,[5]
1、start = 0, end = 0 不执行排序,跳出递归
第七趟,[]
1、start = 2, end = 1 不执行排序,跳出递归

快速排序挖坑填数写法
public static void quickSort1(int[] nums, int start, int end){
	if(nums == null || start >= end) return; 
	int i = start, j = end;
	int pivotNum = nums[start]; //一般都用首位当中枢值,适用于乱序,如果基本有序最好不用首位
	while(i < j){ // 把所有比中枢值大的覆盖到中枢值前边数组位置,比中枢值大的覆盖到后边
		while(i < j && nums[j] > pivotNum) j--; //先从右指针找,比中枢值小的数覆盖左指针位置
		if(i < j) nums[i++] = nums[j]; //i++,让左指针移动到下一位置,因为i位置已经被覆盖
		while(i < j && nums[i] < pivotNum) i++; //再从左指针找,比中枢值大的数覆盖右指针位置
		if(i < j) nums[j--] = nums[i]; //j--,让右指针移动到下一位置,因为j已经被覆盖
	}
	nums[i] = pivotNum;//将中枢值归位
	quickSort1(nums, start, i - 1); //将中枢值前边的数再排一遍直到待排序的数字数目为1
	quickSort1(nums, i + 1, end); //将中枢值后边的数再排一遍直到待排序的数字数目为1
}
快速排序交换写法
public static void quickSort2(int[] nums, int start, int end){
	if(nums == null || start >= end) return;
	int i = start + 1, j = end; //这里不太一样,交换法从左指针第二个数字找起
	int pivotNum = nums[start]; 
	while(i<=j){ //i=j的时候也需要继续移动指针,得到j索引位为数组中比中枢值小的数字最后一位
		while(i <= j && nums[i] <= pivotNum) i++; //比中枢值大的数等待交换
		while(i <= j && nums[j] > pivotNum) j--; //比中枢值小的数等待交换
		if(i < j)
			swap(nums, i , j); //交换左右指针指向的数
	}
	swap(nums, j , start); //中枢值归位
	quickSort2(nums, start, j - 1); //将中枢值前边的数再排一遍直到待排序的数字数目为1
	quickSort2(nums, j + 1, end); //将中枢值后边的数再排一遍直到待排序的数字数目为1
}

public static void swap(int[] nums, int i, int j){
	int temp = nums[i];
	nums[i] = nums[j];
	nums[j] = nums[i];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值