改进快速排序算法Java版

快速排序

【该改进的算法是在本人已有快排算法的基础上改进的:快速排序

(广泛阅读网上资料整理所得,其中重要参考博客:传送门——感谢!)

尽管基本的快速排序已经很有用处了,但是有些情况下,基本的快速排序可能会极为低效,比如排序一个大小为N的有序序列,这样所有的划分就会退化,程序会调用自身N次,所以

·快速排序最坏情况下使用大约(N^2)/2次比较

我们可以对快速排序进行改进,有以下几种方法:

  1. 切换到插入排序

通过观察可见,快速排序的递归调用自身许多的小序列,所以我们可以在遇到小的子序列的时候可以用更好的方法,以此来对快速排序进行改进,比如在子序列小于某个值M时进行插入排序,方法就是把递归出口的条件换成下面这样:

其中M的值根据情况调整。

【通过实验可以证明,当对于多个重复区间操作递归时,发生了栈溢出,而调整M的参数可以有效避免错误。如下图只有M值发生了变化】

2.三路划分的快速排序

对于处理含有大量重复元素的序列,采用原来的方法会产生不必要的操作。我们可以在基准值调整位置的时候,把与基准值相等的放到数组的两端(原来在左边的放左端,原来的右边的放右端)。最后在基准值确定位置的时候,把两端的与基准值相等的元素移动到基准值旁边。这样可以避免关于此基准值多余的操作。

设置两个变量来记录两端的数量:

接着按如上所说:

 

3.三者取中划分

对快速排序算法的另一种改进就是使用一个尽可能在序列中间划分的元素,即基准值key的大小最好是在待排序文件中不大不小

 

有以两种思路:

·一种是避免最坏的情况,使用数组中的一个随机元素作为划分元素,这样出现最坏情况的几率就会相对很小

·一种是从序列中取出三个元素,使用三个元素的中间元素作为划分元素

 

我们这里用的是第二种思路方法,取出数组的左边元素,中间元素和右边元素,然后对这三个元素进行排序,然后以中间的元素作为基准值key。


完整代码如下:


public class QuicksortSup {

	public static void main(String[] args) {
		int[] arr = {4,1,4,4,4,5,2,2,2,2,2,7,5,6,4,4,4,4,4,0,9};
		int len = arr.length;
		
		//从小到大快速排序
		quickSort(arr, 0, len-1);
		
		for(int i=0; i<len; i++) {
			System.out.print(arr[i]+" ");
		}
		System.out.println();
	}

	private static void quickSort(int[] arr, int l, int r) {
		if(l >= r) return ; //只有一个元素或不合法时返回
		
		/*
		 * 改进一:对于少量区间更换更好的排序方法
		 */
		int M = 8;
		if(r - l < M) {
			InsertionSort(arr, l, r);//1.插入排序
			return ;
		}
		
		int i = l;
		int j = r;
		
		/**
		 * 改进三:选取区间的三个值,排序后去中间值当基准值
		 * (为了不影响原来算法的书写,所以把取得的中间值移动到最左端)
		 */
		int mid = l + ((r - l) >> 1);
		if(arr[l] > arr[mid]) swap(arr, l, mid);
		if(arr[l] > arr[r]) swap(arr, l, r);
		if(arr[mid] > arr[r]) swap(arr, mid, r);
		
		swap(arr, l, mid);
		int key = arr[l]; //3.基准值
		
		/**
		 * 改进二:对于与基准值相等的最后移到数组中间,不再参与递归
		 */
		int lLen = l;//2.左区间与基准值相等的标志
		int rLen = r;//2.右区间与基准值相等的标志
		
		while(i < j) {
			while(i < j && arr[j] >= key) //从后开始往回找
				j --;
			while(i < j && arr[i] <= key)
				i ++;
			if(i < j)
				swap(arr, i, j);
			
			//2.把与基准值相等的放到区间两端
			if(arr[i] == key) {
				swap(arr, i, lLen);
				lLen ++;
			}
			if(arr[j] == key) {
				swap(arr, j, rLen);
				rLen --;
			}
		}
		//与基准值交换
		swap(arr, i, l); 
		
		//2.使区间两端与基准值相等的元素移动到基准值位置附近
		for(int u=l; u<lLen; u++) {
			swap(arr, u, --i);
		}
		for(int v=r; v>rLen; v--) {
			swap(arr, v, --j);
		}
		
		quickSort(arr, l, i-1);
		quickSort(arr, i+1, r);
	}
	
	private static void InsertionSort(int[] arr, int l, int r) {
		for(int i=l+1; i<=r; i++) { //要插入的元素下标
			for(int j=l; j<i; j++) {
				if(arr[i] < arr[j]) {
					swap(arr, i, j);
				}
			}
		}
	}

	private static void swap(int[] arr, int i, int j) {
		int t = arr[i];
		arr[i] = arr[j];
		arr[j] = t;
	}

}

如有错误或者不合理的地方,敬请指正~

加油!!

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值