思路
快速排序是一种常见的排序算法,它的基本思路是通过分治的方法将一个大的问题分解成小的问题进行解决。具体而言,快速排序的核心思路是选取一个枢轴元素,将序列分为两个子序列,其中一个子序列的所有元素都比枢轴元素小,而另一个子序列的所有元素都比枢轴元素大,然后对这两个子序列分别进行递归排序,直到子序列的长度为1或0。
在这个实现中,quickSort
方法接受一个整数数组、左边界 left
和右边界 right
作为参数。如果 left
小于 right
,则递归地对左边的子序列和右边的子序列进行排序。partition
方法用于将数组分成两个子序列,并返回枢轴元素的索引。在 partition
方法中,我们选取第一个元素作为枢轴元素,并将数组分成两个子序列。然后,我们使用两个指针 i
和 j
分别从左边和右边开始扫描,直到找到一个大于等于枢轴元素的元素和一个小于等于枢轴元素的元素。然后,我们交换这两个元素,并将指针向中间移动,继续扫描直到指针相遇。最后,我们将枢轴元素与指针 j
所指向的元素交换,并返回 j
作为枢轴元素的索引。
public class QuickSort {
public static void quickSort(int arr[], int left, int right) {
if (left < right) {
// 获得枢轴位置
int pivot = partition(arr, left, right);
// 快速排序左边
quickSort(arr, left, pivot - 1);
// 快速排序右边
quickSort(arr, pivot + 1, right);
}
}
public static int partition(int arr[], int left, int right) {
int pivot = arr[left]; // 枢轴
int i = left + 1;
int j = right;
while (true) {
while (i <= j && arr[i] < pivot)
++i; // 找,直到i<=j或者arr[i]的值小于枢轴值
while (i <= j && arr[j] > pivot)
--j; // 找,直到i<=j或着arr[j]的值大于枢轴值
// 上面的条件不满足了,先看看是不是i>j了
if (i >= j) {
break;
}
// 不是,那么当arr[i]的值小于枢轴值或者arr[j]的值大于枢轴值
// 一个是去左边,一个是去右边
swap(arr, i, j);
++i;
--j;
}
// 一次划分结束,把j和枢轴的位置换一下(应为j就是新的枢轴位置)
// 为什么呢,应为j右边都是大于枢轴的,j左边都是小于枢轴的,那么现在枢轴就应该在j这个位置
// 应为pivot值在left这个位置,所以left和j换
swap(arr, j, left);
// 换完了,返回这次的枢轴值
// 以这个枢轴值为界,进行下次快速排序
return j;
}
public static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[j] ^ arr[i];
疑问
为什么partition代码里最后需要将left和j位置上的值调换呢
在快速排序中,swap(arr, left, j)
是将枢轴元素交换到正确的位置上,即将枢轴元素放在左边子序列和右边子序列之间的位置上。
具体而言,j
是从右边开始扫描的指针,当它扫描到一个小于等于枢轴元素的元素时,它会停止扫描。此时,我们需要将这个元素与枢轴元素交换位置,以便将枢轴元素放在正确的位置上。而枢轴元素原本所在的位置是 left
,因此我们需要将 arr[left]
和 arr[j]
交换位置。
例如,对于数组 [3, 5, 1, 6, 4, 8, 2, 9, 7]
,假设我们选取第一个元素 3
作为枢轴元素,然后执行一次 partition
操作,得到子序列 [2, 1]
、[3]
、[6, 4, 8, 9, 7, 5]
。在这个过程中,j
会停在值为 2
的位置上,然后执行 swap(arr, left, j)
,即交换 arr[0]
和 arr[2]
,得到 [2, 1, 3, 6, 4, 8, 9, 7, 5]
,此时枢轴元素 3
就在子序列 [2, 1]
和 [6, 4, 8, 9, 7, 5]
之间的位置上了。
如果不执行 swap(arr, left, j)
,那么枢轴元素 3
就会留在原地,即子序列 [2, 1, 3]
和 [6, 4, 8, 9, 7, 5]
之间,这样会导致后续的排序操作出现错误。
为什么当它扫描到一个小于等于枢轴元素的元素时,它会停止扫描,明明他前面还有个i<=j的条件,如果该条件不成立同样会跳出循环
在快速排序中,循环条件 i <= j
的作用是保证左右指针不会越界,即在扫描过程中,左指针 i
不会超过右指针 j
的位置。如果左指针 i
超过了右指针 j
,那么循环就应该停止,因为此时扫描过程已经完成。
而当右指针 j
扫描到一个小于等于枢轴元素的元素时,它就停止扫描,因为这个元素应该被放在左边子序列中,而此时左指针 i
可以继续向右扫描。因此,我们需要将枢轴元素放在左右子序列之间的位置上,即将枢轴元素和右指针 j
所指向的元素交换位置,然后左指针 i
继续向右移动。
当左指针 i
扫描到一个大于等于枢轴元素的元素时,它也会停止扫描,因为这个元素应该被放在右边子序列中,而此时右指针 j
可以继续向左扫描。同样地,我们需要将右指针 j
所指向的元素和枢轴元素交换位置,然后右指针 j
继续向左移动。
在快速排序的扫描过程中,左指针 i
和右指针 j
是互相交替向前移动的,因为它们的移动方向是相反的。当左指针 i
和右指针 j
相遇时,扫描过程就结束了,因为此时左右子序列已经被划分好了。最后,我们需要将枢轴元素放在正确的位置上,即将枢轴元素和右指针 j
所指向的元素交换位置。