Java的快速排序算法详解

在进入这个话题之前大家可以想想,如果给你一个无序数组,要你处理成一个有序数组,你会怎么做?
可能是双循环的冒泡排序,可能拆开成多个子集合的再合并的希尔排序,排序算法很多,而现在要介绍的是另一种排序方法
快速排序

快速排序的基本思想

快速排序基本思想就是分而治之,分而治之是什么意思不清楚?别急我们现在就来探索一下。

分而治之

我们只需要明白,分而治之就是把一个大的问题分成多个小问题来解决的一种思路,最常用的就是递归了。

如何把分而治之应用在排序上?

在讲这个的时候,举个栗子或许会更加容易理解些。(如不想看或已了解可跳过,实现代码在最后

大家上学的时候应该都经历过这样的一种场景:排座位前的按身高排队。老师会让整个班级的人去门外走廊上排成一队,但是同学比较调皮不按身高从低到高依次排列,那么这个时候就需要老师来把队伍整理成有序的了。

加入老师以这样的方式来排序:老师先从队伍中随便找出一个同学,我们称为A。然后告诉同学们:比A的站左边,比A的站右边。那么这样一来,左边的都是比这个同学矮的,右边都是比这个同学高的。类似于以下这样
在这里插入图片描述
所以剩下的任务就是把A 左边和右边的同学 分为两队重复上述方式排序好。
这个时候你是不是想到了,那不就是递归吗?没错,就是递归。

如何实现快速排序

思路有了,现在就要把思路变成代码,并且实现了。我们先看看上述有几个过程。

第一步:找出一个同学A,利用他作为标准,把所有同学分成两组,一组在左比他矮,一组在右比他高。

第二步:把除A以外的其他同学,分为两组。重复上述过程,直到整个队伍,排序完成,变成有序的队伍。

好的,我们现在知道大概的步骤了,想想怎么实现吧。先看第一步,在第一步中我们做了,两个操作。
1、找出分队标准。先找出一个同学,我们称之为A,用作其他同学比较的标准。这个简单,因为队伍是无序的,我们可以取队伍最前面的人作为标准。
2、比较分队,找到A应该呆在的位置,并且以A为中点分队。将所有同学与A比较,将队伍以A为中心分为两队。我们常规的思路可能是,一个一个比较,比A矮的站左边,比A高的站右边。但是为了避免这种自由交换过程中学生们互相聊天、玩闹,打扰到楼下其他班级正在上课的同学,所以交换位置由老师主持,每次交换两个人的位置。老师的具体做法如下:先从队伍的末尾往前,找出一个比A矮的,再从队伍最前往后找出一个比他高的,并且记录这两人的位置,然后交换这两人的位置

重复此操作,直到遇到这样的一种情况:假设从末尾开始找的比A矮的同学为B,从前往后找到的比A高的同学为C。因为B是从后往前找的,那么B的位置大概率是在C的后面,当B在C的后面时,正好交换两人的位置。当找到的B在C前面时呢?
我们看这个图:
在这里插入图片描述
这种情况就是交换B、C的位置即可
但是怎么确定A应该在队伍里的何处呢?当找到的B(从末尾往前找比A矮的)在C(从前往后找比A高的)前面时,那么A的位置是不是找到了,如下图
在这里插入图片描述
那么这个时候,A应该与谁交换位置,才能使得队伍左边比A矮,队伍右边比A高呢?
我们回头看看老师的做法:先从队伍的末尾往前,找出一个比A矮的–B同学,再从队伍最前往后找出一个比他高的–C同学,并且记录这两人的位置,然后交换这两人的位置

也就意味着,B的身高是比A矮的,C的身高是比A高的,我们要做的是比A矮的站左边,比A高的站右边,我们毫无疑问是交换B和A。这样的话,C不动,比A高,B换到第一位比A矮,这样就队伍以A为中心分开了。如果交换C呢?因为C比A高,换到左边,是不是不符合排队从矮到高的要求?

至此,整个队伍就被分成了以A为中心两队。
在这里插入图片描述
再把比A矮的同学和比A高的同学,按以上方法,排序就可以了。现在我们假如比A矮的同学有5人,比A高的同学有5人。那么现在需要先对左边5人继续排序,那么我们再用上面的方法,5人队伍分为两队,左边两人队,右边也是两人队伍以及作为标准在中间的同学。然后再分,再把左边的两人队伍分为一人队伍和作为标准的同学。
如下图()只分析左侧
在这里插入图片描述
在这里插入图片描述
对两人队伍的分析,有两种情况,一种是剩下的那位在作为标准值的同学左侧,另一种是在他的右侧。我们假设为在左侧,如下图
在这里插入图片描述
那么当以标准值的同学一侧的队伍只有一个人时,这支队伍就排好序了。

如果把作为标准的同学拿出来,你就会发现,作为标准的同学身高从前往后是有序的。你如果把整个分队过程画完,你就会发现,一人队伍和作为标准的同学,就是一个有序的队伍了。

以上就是整个快速排序的思路了。
伪代码代码见下图

public static void quickSort(int nums[] , int left , int right) {
		//方法是否已达出口
		
		//找基准值 把数组分为左右两部分
		
		//交换基准值与找到的右侧的位置 右侧值比基准值小
		
		//对左右半边执行同样的操作 
		
} 

具体实现如下:

/**
	 * @param nums
	 * 数组
	 * @param left
	 * 左侧边界
	 * @param right
	 * 右侧边界
	 * @return
	 */
	public static void quickSort(int nums[] , int left , int right) {
		//方法出口
		if(left >= right) {
			return;
		}
		//baseValue-基准值
		//temp-用于交换位置的临时变量
		//i-从左侧开始寻找大于基准值的下标
		//j-从右侧开始寻找小于基准值的小于基准值的下标
		int i,j,baseValue,temp;
		baseValue = nums[left];//基准值设为左侧第一位
		i = left; 
		j = right;
		//找出基准值 把数组分为左右两部分
		while(i < j) {
			//从末尾往前找出小于基准值的数 i < j 保证 i在j的左侧
			while(nums[j] >= baseValue && i < j) {
				j--;
			}
			//从起始位置往右找出大于基准值的数
			while(nums[i] <= baseValue && i < j) {
				i++;
			}
			//若i<j交换左右值的位置
			if(i < j) {
				temp = nums[i];
				nums[i] = nums[j];
				nums[j] = temp;
			}
		}
		
		//交换基准值与找到的右侧的位置 右侧值比基准值小
		temp = nums[left];
		nums[left] = nums[j];
		nums[j] = temp;
		
		//对左右半边执行同样的操作 需要使用左右侧边界的值
		quickSort(nums , left , j - 1);//左侧排序
		quickSort(nums , j + 1 , right);//右侧排序
	}

	public static void main(String[] args) {
		//无序数组
		int nums[] = {9,18,4,27,3,21,15,18,34,5,14};
		//倒序数组
		int nums2[] = {10,9,8,7,6,5,4,3,2,1};
		//同值数组
		int nums3[] = {3,3,3,3,3,3,3,3,3,3};
		//打印排序前数组
		System.out.println("无序数组排序前");
		for(int i = 0; i <= nums.length - 1; i++) {
			if(i != nums.length - 1)
				System.out.print(nums[i] + ", ");
			else
				System.out.println(nums[i]);
		}
		
		//快速排序
		quickSort(nums, 0, nums.length - 1 );
		
		//打印排序后数组
		System.out.println("无序数组排序后");
		for(int i = 0; i <= nums.length - 1; i++) {
			if(i != nums.length - 1)
				System.out.print(nums[i] + ", ");
			else
				System.out.println(nums[i]);
		}
		
		
		//打印排序前数组
		System.out.println("倒序数组排序前");
		for(int i = 0; i <= nums2.length - 1; i++) {
			if(i != nums2.length - 1)
				System.out.print(nums2[i] + ", ");
			else
				System.out.println(nums2[i]);
		}
		
		//快速排序
		quickSort(nums2, 0, nums2.length - 1 );
		
		//打印排序后数组
		System.out.println("倒序数组排序后");
		for(int i = 0; i <= nums2.length - 1; i++) {
			if(i != nums2.length - 1)
				System.out.print(nums2[i] + ", ");
			else
				System.out.println(nums2[i]);
		}
		
		System.out.println("同值数组排序前");
		for(int i = 0; i <= nums3.length - 1; i++) {
			if(i != nums3.length - 1)
				System.out.print(nums3[i] + ", ");
			else
				System.out.println(nums3[i]);
		}
		
		//快速排序
		quickSort(nums3, 0, nums3.length - 1 );
		
		//打印排序后数组
		System.out.println("同值数组排序后");
		for(int i = 0; i <= nums3.length - 1; i++) {
			if(i != nums3.length - 1)
				System.out.print(nums3[i] + ", ");
			else
				System.out.println(nums3[i]);
		}
		
	}

当然以上代码还可对输入的数组做一下非空判断,对于输入空数组的情况直接结束循环。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值