快速排序-Java实现排序算法(一)

0x00 为什么是排序

排序是比较重要的内容,面试是必须准备的。今天先实现int数组的快排,之后再增加泛型(一种简单的方法就是使用Comparable接口,但是排序的对象需要实现Comparable接口)。

 0x01 实现

java实现代码:

	public static void QuickSort(int[] data, int start, int end){
		if (start >= end) return;
	    	int key = data[start];
		int i = start;
		int j = end;
		while (i < j) {
			while (data[j] > key && i < j) j--;
			data[i] = data[j];
			while (data[i] < key && i < j) i++;
			data[j] = data[i];
		}
		data[i] = key;
		QuickSort(data, start, i-1);
		QuickSort(data, j+1, end);
	} 

快速排序一般基于递归实现。其思路是这样的:
1.选定一个合适的值(理想情况中值最好,但实现中一般使用数组第一个值),称为“枢轴”(pivot)。
2.基于这个值,将数组分为两部分,较小的分在左边,较大的分在右边。
3.可以肯定,如此一轮下来,这个枢轴的位置一定在最终位置上。
4.对两个子数组分别重复上述过程,直到每个数组只有一个元素。
5.排序完成。

算法稳定性

快速排序并不是稳定的。这是因为我们无法保证相等的数据按顺序被扫描到和按顺序存放。

算法优化

上面这个快速排序算法可以说是最基本的快速排序,因为它并没有考虑任何输入数据。但是,我们很容易发现这个算法的缺陷:这就是在我们输入数据基本有序甚至完全有序的时候,这算法退化为冒泡排序,不再是O(n㏒n),而是O(n^2)了。

究其根源,在于我们的代码实现中,每次只从数组第一个开始取。如果我们采用“三者取中”,即arr[low],arr[high],arr[(low+high)/2]三者的中值作为枢轴记录,则可以大大提高快速排序在最坏情况下的性能。但是,我们仍然无法将它在数组有序情形下的性能提高到O(n)。还有一些方法可以不同程度地提高快速排序在最坏情况下的时间性能。

此外,快速排序需要一个递归栈,通常情况下这个栈不会很深,为log(n)级别。但是,如果每次划分的两个数组长度严重失衡,则为最坏情况,栈的深度将增加到O(n)。此时,由栈空间带来的空间复杂度不可忽略。如果加上额外变量的开销,这里甚至可能达到恐怖的O(n^2)空间复杂度。所以,快速排序的最差空间复杂度不是一个定值,甚至可能不在一个级别。

为了解决这个问题,我们可以在每次划分后比较两端的长度,并先对的序列进行排序(目的是先结束这些栈以释放空间),可以将最大深度降回到O(㏒n)级别。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据