快速排序(英语:Quicksort),又称分区交换排序(partition-exchange sort),简称快排,一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序n个项目要O(n logn) O(nlog n)(大O符号)次比较。在最坏状况下则需O(n^2)次比较,但这种状况并不常见。事实上,快速排序O(n logn)通常明显比其他算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地达成。
-----《维基百科》
快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。
步骤为:
- 挑选基准值:从数列中挑出一个元素,称为“基准”(pivot),在这篇博客中使用要排序的数列的中间位置元素作为pivot。
- 分割:重新排序数列,所有比基准值小的元素摆放在基准左边,所有比基准值大的元素摆在基准右边(然而与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成。只要某个元素的左边所有的元素都小于或者等于这个元素,并且该元素的右边的所有元素都大于等于该元素,那么该元素的位置就是整个数列排序后的最终位置。
- 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。
其中最重要的是如何实现分割,既该如何将数列里所有小于基准的元素放在其左边,而把所有大于基准的元素放在其右边。 其实就是同过数值的交换来实现,就类似冒泡排序的两两交换,但又不同的是,快速排序实现了跨越式的交换,这样就能避免一些不必要的交换,从而大大提高了执行效率。
先用 l ( l 是L的小写) 指向指向待排序数列中第一个元素,r指向待排序数列的最后一个元素,pivot要等于待排序数列的中间元素(注意:这里的pivot存储的是数值,而不是下标,原因会在代码中给出),在 l < r的条件下,如果arr[i]整体的排序过程:
代码:
package hxz.Sort;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {4,3,6,7,5,2,1,9,8};
QuickSort.quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr, int left, int right) {
int l = left;
int r = right;
int pivot = arr[(left+right)/2]; //为什么pivot不能用arr[left+right]来代替?? 例子:假如在只有两个数的情况下:
//2 1 pivot指向2,l也指向了2,r指向了1,l不移动,r向左移动一位,r==l,跳出循环,那么2 1就是排序失败的结果
int temp;
while(l<r) {
while(arr[l]<pivot) {
l++;
}
while(arr[r]>pivot) {
r--;
}
if(l>=r) {//if(l==r)??? 代表着pivot左右两边的值都<=或者都>=pivot
break;
}
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
/*当arr[l]==arr[pivot]或者arr[r]==arr[pivot]时,如果不对下标l或r进行l++,r--操作的话那么在后面的程序
* 运行过程中,l或者r将不会移动(因为他们不会再进入下面的循环:
* while(arr[l]<arr[pivot]) {
l++;
}
while(arr[r]>arr[pivot]) {
r--;
}),循环将一直进行下去,例子:arr={4,3,5,8,5,6,1,5,9}
*/
if(arr[l]==pivot) {
r--;//注意是r--,因为l和r交换值之后,r指向的就是l原先指向的值
}
if(arr[r]==pivot) {
l++;
}
}
//while循环结束,代表着pivot左右两边的值都<=或者都>=pivot
//System.out.println(Arrays.toString(arr)); 打印每一次数列排序后的结果
if(l==r) {
l++; r--;
}
if(left<r) {
quickSort(arr, left, r);
}
if(l<right) {
quickSort(arr, l, right);
}
}