快排
快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
-
首先设定一个分界值,通过该分界值将数组分成左右两部分.
-
将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值.
-
然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理.
-
重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
性能
快速排序的一次划分算法从两头交替搜索,直到low和high重合,因此其时间复杂度是O(n);而整个快速排序算法的时间复杂度与划分的趟数有关。
理想的情况是,每次划分所选择的中间数恰好将当前序列几乎等分,经过log2n趟划分,便可得到长度为1的子表。这样,整个算法的时间复杂度为O(nlog2n)。
最坏的情况是,每次所选的中间数是当前序列中的最大或最小元素,这使得每次划分所得的子表中一个为空表,另一子表的长度为原表的长度-1。这样,长度为n的数据表的快速排序需要经过n趟划分,使得整个排序算法的时间复杂度为O(n2)。
可以证明,快速排序的平均时间复杂度也是O(nlog2n)。因此,该排序方法被认为是目前最好的一种内部排序方法。
从空间性能上看,尽管快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。最好的情况下,即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1);但最坏的情况下,栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n))。
实现:
public class QuickSort {
public static void main(String[] args) {
int[] array = { 12, 2, 5, 654, 73, 8, 14, 95, 0, 53, -2 };
sort(array, 0, array.length - 1);
System.out.println(Arrays.toString(array));
}
public static void sort(int[] array, int left, int right) {
int l = left;
int r = right;
int temp = 0;
int value = array[(left + right) / 2];// 取中间位置的值做位置排序的衡量值
while (l < r) {// 当左面指针小于右面的指针时即未分离完成
while (array[l] < value) {// 找到大于左面队列的值停止,即找到大于value的值等待后面将其与右面小于value的值调换
l++;
}
while (array[r] > value) {// 找到右面小于value的值等待调换
r--;
}
if (l >= r) {// 说明两边不需要调换直接即可结束
break;
}
temp = array[l];
array[l] = array[r];
array[r] = temp;
if (array[l] == value) {// 左面指针已经指到最前,无需再进行操作,继续右面指针向前移动
r--;
}
if (array[r] == value) {// 右面指针已经指到最前,无需再进行操作,继续左面指针向前移动
l++;
}
}
if (l == r) {// 如果指向同一处,证明两边已经不需调换位置
l++;// 此处设置为了跳出while,否则栈溢出
r--;
}
if (left < r) {
sort(array, left, r);// 将左面的数组继续之前的方式进行调换
}
if (l < right) {
sort(array, l, right);// 将右面的数组继续之前的方式进行调换
}
}
}