
快速排序(quicksort)是一种高效的、基于比较的排序算法,其核心思想是“分而治之”。它通过选择一个“基准”(pivot)元素,将数组划分为两个子数组:一个子数组中的所有元素都小于基准,另一个子数组中的所有元素都大于基准。然后,对这两个子数组递归地进行快速排序,直到整个数组有序。
一个典型的快速排序实现包含以下几个关键步骤:
以下是一个尝试实现快速排序的Java代码示例,但在实际运行中发现排序结果不正确:
public class QuickSort {
public static void quickSort(int[] s) {
quickSortSub(s, 0, s.length - 1);
}
private static void quickSortSub(int[] s, int a, int b) {
// 原始问题:当子数组长度为2时(b-a=1),此条件不满足,导致无法排序
if(b-a > 1) {
int point = partition(s, a, b);
quickSortSub(s, a, point - 1);
quickSortSub(s, point + 1, b);
}
}
private static int partition(int[] s, int a, int b) {
int pivot = s[b]; // 选择最后一个元素作为基准
int left = a;
int right = b-1;
while(left < right) { // 原始问题:当right越过a时,循环可能继续,导致left和right指向相同元素时仍尝试交换
while(s[left] < pivot) {
left++;
}
while(s[right] > pivot) {
right--;
}
if(left < right) { // 避免left和right交叉后进行交换
int tmp = s[left];
s[left] = s[right];
s[right] = tmp;
}
}
// 原始问题:此处将基准元素 s[b] 与 s[left] 交换,但没有检查 s[left] 是否真的大于或等于基准
s[b] = s[left];
s[left] = pivot;
return left;
}
public static void main(String[] args) {
int[] arr = {85, 10, 24, 63, 45, 27, 100, 31, 96, 50, 40, 23, 49, 96, 120, 105, 13, 5, 42, 69, 22, 12};
quickSort(arr);
for (int i: arr) System.out.print(i + ", ");
System.out.println("");
// 预期输出:5, 10, 12, 13, 22, 23, 24, 27, 31, 40, 42, 45, 49, 50, 63, 69, 85, 96, 96, 100, 105, 120
// 实际输出:5, 10, 12, 22, 13, 23, 24, 31, 27, 40, 42, 45, 49, 50, 63, 69, 85, 96, 96, 100, 120, 105 (部分元素未正确排序)
}
}上述代码的输出显示数组并未完全排序。通过分析,我们发现主要问题出在 quickSortSub 的递归终止条件和 partition 方法的循环逻辑及最终交换上。
针对上述问题,我们对代码进行以下修正:
立即学习“Java免费学习笔记(深入)”;
quickSortSub 方法的递归终止条件:
quickSortSub 方法的递归调用前检查:
partition 方法的循环条件:
partition 方法的基准元素最终归位:
public class QuickSort {
public static void quickSort(int[] s) {
quickSortSub(s, 0, s.length - 1);
}
private static void quickSortSub(int[] s, int a, int b) {
// 修正1: 确保子数组长度为2时也能进入排序
if (b - a >= 1) {
int point = partition(s, a, b);
// 修正2: 递归调用前检查子数组是否至少有两个元素,避免不必要的递归
if (point >= a + 1) { // 至少有一个元素 (point-1 >= a)
quickSortSub(s, a, point - 1);
}
if (point <= b - 1) { // 至少有一个元素 (point+1 <= b)
quickSortSub(s, point + 1, b);
}
}
}
private static int partition(int[] s, int a, int b) {
int pivot = s[b]; // 选择最后一个元素作为基准
int left = a;
int right = b - 1;
// 修正3: 增加 right > a 条件,防止 right 越界,并确保left和right指针正确移动
while (left <= right) { // 循环条件应为 <=,当 left == right 时也可能需要处理
while (left <= right && s[left] < pivot) { // left不能越过right
left++;
}
while (left <= right && s[right] > pivot) { // right不能越过left
right--;
}
if (left <= right) { // 确保left和right未交叉或重合时才进行交换
int tmp = s[left];
s[left] = s[right];
s[right] = tmp;
left++; // 交换后,移动指针
right--; // 交换后,移动指针
}
}
// 修正4: 将基准元素放到正确的位置
// 循环结束后,left 指针指向第一个大于或等于 pivot 的元素,
// 或者如果所有元素都小于 pivot,则 left 会越过 b。
// right 指针指向最后一个小于或等于 pivot 的元素。
// 此时,left 指针是基准元素应该放置的位置。
// 将基准元素 s[b] 与 s[left] 交换。
int tmp = s[left];
s[left] = s[b];
s[b] = tmp;
return left; // 返回基准元素的新位置
}
public static void main(String[] args) {
int[] arr = {85, 10, 24, 63, 45, 27, 100, 31, 96, 50, 40, 23, 49, 96, 120, 105, 13, 5, 42, 69, 22, 12};
System.out.println("原始数组:");
for (int i : arr) System.out.print(i + ", ");
System.out.println("\n");
quickSort(arr);
System.out.println("排序后数组:");
for (int i : arr) System.out.print(i + ", ");
System.out.println("");
}
}修正后的 partition 方法的另一种常见且更简洁的实现方式(Hoare分区方案):
private static int partitionHoare(int[] s, int a, int b) {
int pivot = s[a]; // 选择第一个元素作为基准
int left = a - 1;
int right = b + 1;
while (true) {
do {
left++;
} while (s[left] < pivot);
do {
right--;
} while (s[right] > pivot);
if (left >= right) {
return right; // 返回基准的最终位置
}
// 交换 s[left] 和 s[right]
int tmp = s[left];
s[left] = s[right];
s[right] = tmp;
}
}注意: 如果使用 partitionHoare 这种方式,quickSortSub 的递归调用需要调整为 quickSortSub(s, a, point); 和 quickSortSub(s, point + 1, b);,因为 point 返回的是最后一个小于或等于基准的元素的索引,而不是基准本身的索引。
为了保持与原问题及修正思路的一致性,我们继续优化最初的 Lomuto 分区方案。上述修正后的代码中的 partition 方法已经包含了对 left 和 right 指针的正确处理以及基准的最终归位。
通过对一个存在问题的Java快速排序实现进行细致的分析和修正,我们深入理解了递归快速排序算法在分区逻辑、递归边界条件以及基准元素归位等方面的常见陷阱。一个健壮的快速排序实现需要精确控制 left 和 right 指针的移动,确保循环条件的正确性,并在递归调用时进行必要的边界检查。掌握这些关键点,能够帮助开发者构建高效且稳定的快速排序算法。
以上就是Java递归快速排序算法的优化与调试指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号