基本思想
选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分。其中左半部分都比基准数小,右半部分都比基准数大。这样经过一次排序就能确定一个基准数的最终位置。递归地对左右两部分做同样的操作,每一次操作都能确定一个元素的位置,直到所有元素的位置都被确定,排序完成。
Partition函数
快排最重要的就是Partition函数,网上的版本有很多,虽说结果一致但步骤不尽相同。下面的是我最喜欢的一种方式:
在每一趟中:
-
把首元素作为基准值
-
左指针初始时指向基准值,但循环刚开始时有一个++i,说明其实是从首元素的下一个位置开始比较的
-
右指针初始时指向数组最后一个元素的下一个位置,但循环刚开始时有一个--j,说明其实是从尾元素开始比较的
-
先向后移动左指针,直至找到一个不小于基准值的元素,停下来(先不与任何元素交换)
-
再向前移动右指针,直至找到一个小于基准值的元素,停下来(先不与任何元素交换)
-
检查当前两个指针是否重合或者早已穿过彼此,若没有,则交换两个指针所指向的元素的位置(指针不动),然后继续移动两个指针(回到第4,5步);若是,则break跳出循环,此时右指针所指向的元素就是想要的分裂点**,**将该分裂点与基准值交换位置,并返回原分裂点位置 j 。(只能是右指针,不能是左指针)
实现代码如下:
public static int Patition(int[] arr, int low, int high) {
int i = low, j = high + 1; // 可以"high+1" 是因为后面用到了--j
int x = arr[low]; // 基准值
while (true) {
while (arr[++i] <= x && i < high) ; // 从左往右找大于基准值x的数的位置i,找不到的话i==r
while (arr[--j] > x && j>low) ; // 从右往左找小于等于基准值x的数的位置i,找不到的话j==p
if (i >= j)
break;
swap(arr, i, j);
}
swap(arr, low, j); //因为最开始用到了++i,所以基准值至始至终都没有参与换位,所以可以直接拿low来交换
return j;
}
全部代码:
import java.util.ArrayList;
import java.util.Arrays;
public class Solution {
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
public static int Patition(int[] arr, int low, int high) {
int i = low, j = high + 1;
int x = arr[low];
while (true) {
while (arr[++i] <= x && i < high) ;
while (arr[--j] > x && j > low) ;
if (i >= j)
break;
swap(arr, i, j);
}
swap(arr, low, j);
return j;
}
public static void QuickSort(int[] arr, int low, int high) {
if (low < high) {
int pivot = Patition(arr, low, high);
QuickSort(arr, low, pivot - 1);
QuickSort(arr, pivot + 1, high);
}
}
public static void main(String[] args) {
int[] arr = {6,7,5,2,5,8};
int len = arr.length;
QuickSort(arr, 0, len - 1);
System.out.println(Arrays.toString(arr));
}
}
示例