快速排序(Java实现)及其改进(三向切分的快速排序)

快速排序思想:通过一趟排序将数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行。
一趟快速排序的步骤:

  1. 设置两个变量:i = 0,j = N - 1(第一次排序时i,j的值,N为元素个数);
  2. 以第一个数组元素作为关键数据,赋值给key,即key=a[i](此时a[i]的值已有key保存,故i位置可被其他赋值);
  3. 从j开始向前搜索(j–),找到第一个小于key的值a[j],将a[j]赋值给a[i](此时a[j]可用来赋值),然后i++;
  4. 从i开始向后搜索(i++),找到第一个大于key的值a[i],将a[i]赋值给a[j](此时a[i]可用来赋值),然后j–;
  5. 重复3.4.步骤,直到i==j,并将key赋给此位置,使得i(等于j)左边的元素都小于key,右边的元素都大于key。

算法性能:时间复杂度O(NlogN),空间复杂度O(lgN),不稳定的排序。
快速排序是最快的通用排序算法,因为它的内循环中的指令少。
Java代码实现:

public static void sort(int[] a){
	sort(a, 0, a.length - 1);
}
private static void sort(int[] a, int low, int high){
	if(low < high){
		int mid = partition(a, low, high);//切分,即一趟排序过程
		sort(a, low, mid - 1);//将左半部分排序
		sort(a, mid + 1, high);//将右半部分排序
	}
}
private static int partition(int[] a, int low, int high){
	int key = a[low];
		
	while(low < high){
		while(low < high && a[high] >= key){
			high--;
		}
		if(low < high){
			a[low++] = a[high];
		}
		while(low < high && a[low] <= key){
			low++;
		}
		if(low < high){
			a[high--] = a[low];
		}
	}
	a[low] = key;
		
	return low;
}

在有大量重复元素的情况下,快速排序的递归性会使元素全部重复的子数组经常出现,一个改进的思想是将数组分成三部分,分别对应小于、等于和大于切分元素(key)的数组,即三向切分的快速排序。
从左到右遍历数组,维护一个下标lt使得a[low…lt-1]中的元素都小于key,一个下标gt使得a[gt+1…high]中的元素都大于key,一个下标i使得a[lt…i-1]中的元素都等于key,a[i…gt]中的元素还未确定,如图所示。
三向切分的快速排序示意图
一开始i和low相等,对a[i]进行三向比较处理以下情况:

  1. a[i]小于key,将a[lt]和a[i]交换,将lt和i加1;
  2. a[i]大于key,将a[gt]和a[i]交换,将gt减1;
  3. a[i]等于key,将i加1。

这些操作使得gt-i不断缩小(这样循环才会结束)。
时间复杂度为N~NlogN,java.util.Arrays.sort()方法对基本数据类型就使用了三向切分的快速排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值