首先,用一句话介绍快速排序——选出一个基准值,用基准值将数组分为两边,左边小于基准值,右边大于基准值,然后又将左边和右边进行同样的操作,直到整个数组有序。
下面用易于理解的方式阐释一下快速排序的大致步骤:
这里将小的这边称为(左派),大的这边称为(右派)
- 选择一个基准值(一般是第一个元素) (左派的担保)
- 右派自右向左找一个数小于基准数,然后交给左派 (借给左派)
- 左派自左向右找一个数,交给右派 (还给右派)
- 最终可能存在的两种状况:
4.1 左派找到的大于基准数的个数与右派找到的小于基准数的个数相等 (左派还清债务,收回担保)
4.2 左派找到的大于基准数的个数小于右派找到的小于基准数的个数 (左派未还清债务,交换担保)
4.3 注意:是不存在大于这种可能的,清理清思路,是右派借,左派还,右派借,左派还…(关键在于左派能否还清)
上面并未提到左派如何处理右派借,以及右派如何处理左派还,这里很好理解:
左派和右派其实是两个数组,左派先把基准值支出去,那么左派就有一个空,所以右派借来的就用于补左派的空,于是右派又有了一个空,于是左派就还了一个用于补右派的空。以此反复,直到左右大小区分,再用基准值去补最后一个空。
这其实和拼图游戏是很像的,缺口的数量是没有变的(只有一个),变的只是缺口的位置(左派和右派来回变动)。我们的目标也是从无序到有序。
下面举一个栗子
下面是待排序数组
1) 选择基准值(担保):62 (注意缺口的数是没有任何意义的)
2)右派从44向左找一个数小于基准值:44(填到62这个缺口)
3)左派从72向右找一个数大于基准数:72(填到44这个缺口)
1)右派从44向左找一个数小于基准值:5(填到72这个缺口)
2)左派从72向右找一个数大于基准数:89(填到5这个缺口)
1)右派从44向左找一个数小于基准值:24(填到89这个缺口)
2)左派从72向右找一个数大于基准数:67(填到24这个缺口)
找完了,这时左派找到的大于基准数的个数与右派找到的小于基准数的个数相等(将基准值填到67这个缺口)——也就是上面结果的第一种,这里就不举第二个例子了
然后就是将左派与右派按照同样的步骤,
有兴趣的可以用excel等软件像我一样打草稿弄懂过程(注意这个是草稿)
下面是代码,网上有许多不同的代码,主要是思想清晰,所以这里只给出快速排序的一种实现方案
package title;
/**
* 快速排序
*/
public class QuickSoft {
public static void quickSort(int[] arr) {
if (arr != null && arr.length > 1)
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int left, int right) {
if (left < right) {
int tmp = arr[left]; // 记录基准数
int l = left;
int r = right;
while (l < r) {
// 从右往左找一个数小于基准数
// 1) 如果从右往左找到了一个数小于基准数(l < r)
// 2)如果从右往左未找到一个数小于基准数 (l = r)
while (l < r && arr[r] > tmp)
r--;
if (l < r)
arr[l++] = arr[r];
// 从左往右找一个数大于基准数
// 1.1)如果从左往右找到了一个数小于基准数(l < r)—— 还清债务
// 1.2)如果从左往右没有找到一个数小于基准数 (l = r)—— 未还清债务
// 2)由于l = r,后面的代码不执行(l = r)—— 表示基准数是最小值
while (l < r && arr[l] < tmp)
l++;
if (l < r)
arr[r--] = arr[l];
}
// 1)如果还清债务,左派收回担保
// 1.2)如果未还清债务,由于此时l = r,且都在右派的缺口处,因此右派收回担保
arr[l] = tmp;
quickSort(arr, left, l - 1);
quickSort(arr, l + 1, right);
}
}
public static void main(String[] args) {
int[] arr = {62, 56, 45, 46, 24, 43, 24, 28, 5, 44};
quickSort(arr);
for (int i : arr)
System.out.print(i + " ");
}
}
注意上面的
1)对应1.1)1.2)
2)对应2)