内容纲要
快速排序 Java 实现
public class QuickSort {
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// 获取分区点
int pivotIndex = partition(arr, low, high);
// 递归排序左子数组
quickSort(arr, low, pivotIndex - 1);
// 递归排序右子数组
quickSort(arr, pivotIndex + 1, high);
}
}
private static int partition(int[] arr, int low, int high) {
// 选择最右边的元素作为基准
int pivot = arr[high];
// i 是小于基准的区域的边界
int i = low - 1;
for (int j = low; j < high; j++) {
// 如果当前元素小于等于基准
if (arr[j] <= pivot) {
i++;
// 交换元素
swap(arr, i, j);
}
}
// 将基准元素放到正确的位置
swap(arr, i + 1, high);
return i + 1;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String[] args) {
int[] arr = {10, 7, 8, 9, 1, 5};
System.out.println("排序前:");
printArray(arr);
quickSort(arr, 0, arr.length - 1);
System.out.println("排序后:");
printArray(arr);
}
private static void printArray(int[] arr) {
for (int value : arr) {
System.out.print(value + " ");
}
System.out.println();
}
}
快速排序 Python 实现
def quick_sort(arr, low, high):
if low < high:
# 获取分区点
pivot_index = partition(arr, low, high)
# 递归排序左子数组
quick_sort(arr, low, pivot_index - 1)
# 递归排序右子数组
quick_sort(arr, pivot_index + 1, high)
def partition(arr, low, high):
# 选择最右边的元素作为基准
pivot = arr[high]
# i 是小于基准的区域的边界
i = low - 1
for j in range(low, high):
# 如果当前元素小于等于基准
if arr[j] <= pivot:
i += 1
# 交换元素
arr[i], arr[j] = arr[j], arr[i]
# 将基准元素放到正确的位置
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1
# 测试代码
if __name__ == "__main__":
arr = [10, 7, 8, 9, 1, 5]
print("排序前:", arr)
quick_sort(arr, 0, len(arr) - 1)
print("排序后:", arr)
逐行代码解析
1. 主方法 quickSort (Java) / quick_sort (Python)
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pivotIndex = partition(arr, low, high);
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
}
}
def quick_sort(arr, low, high):
if low < high:
pivot_index = partition(arr, low, high)
quick_sort(arr, low, pivot_index - 1)
quick_sort(arr, pivot_index + 1, high)
- 作用:递归实现快速排序
- 参数:
arr:待排序数组low:当前排序的起始索引high:当前排序的结束索引
- 条件
low < high:确保至少有两个元素需要排序,否则递归终止 - 步骤:
- 调用
partition方法进行分区,得到基准元素的最终位置pivotIndex - 递归排序左半部分(从
low到pivotIndex-1) - 递归排序右半部分(从
pivotIndex+1到high)
- 调用
2. 分区方法 partition (Java) / partition (Python)
private static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
swap(arr, i, j);
}
}
swap(arr, i + 1, high);
return i + 1;
}
def partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1
- 作用:将数组分为两部分,使得左边部分都小于等于基准,右边部分都大于基准,并返回基准元素的最终位置
- 步骤解析:
- 选择基准:
int pivot = arr[high];选择最右边的元素作为基准 - 初始化指针:
int i = low - 1;指针i初始化为low-1,表示小于基准的区域的边界(初始时该区域为空) - 遍历数组:
for (int j = low; j < high; j++)遍历从low到high-1的元素(因为high位置是基准,最后处理) - 比较元素:
if (arr[j] <= pivot)如果当前元素小于等于基准,则:i++:将边界指针i向右移动一位swap(arr, i, j):将当前元素arr[j]交换到i的位置(即小于基准的区域)
- 放置基准:遍历结束后,将基准元素
arr[high]与i+1位置的元素交换:- 此时
i+1左边的元素都小于等于基准 i+1右边的元素都大于基准
- 此时
- 返回基准位置:
return i + 1;返回基准元素的最终位置
- 选择基准:
3. 交换方法 swap (Java) / Python 的元组交换
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
# Python 中直接使用元组交换,无需单独方法
arr[i], arr[j] = arr[j], arr[i]
- 作用:交换数组中两个位置的元素
- Java:使用临时变量
temp完成交换 - Python:利用元组解包特性,直接交换
关键条件解析
1. 递归终止条件:if (low < high)
- 为什么需要这个条件?
- 当
low >= high时,表示子数组长度为0或1,已经有序,无需排序 - 避免无限递归和栈溢出
- 当
- 示例:
- 初始调用:
quickSort(arr, 0, 5)(数组长度6) - 递归调用:
quickSort(arr, 0, 2)(子数组长度3) - 终止条件:
quickSort(arr, 0, 0)(子数组长度1,直接返回)
- 初始调用:
2. 分区循环:for (int j = low; j < high; j++)
- 为什么是
j < high而不是j <= high?high位置的元素是基准,最后单独处理- 避免在循环中处理基准元素,确保基准元素在最后被放置到正确位置
- 示例:
- 数组
[10, 7, 8, 9, 1, 5],high=5(基准是5) - 循环遍历
j从0到4(索引5的基准不参与循环比较)
- 数组
3. 指针 i 的初始化:int i = low - 1
- 为什么是
low - 1?i表示小于基准的区域的边界- 初始时该区域为空,所以边界在
low-1位置 - 第一次遇到小于基准的元素时,
i++变为low,然后交换到low位置
- 示例:
low=0,初始i=-1- 遇到第一个小于基准的元素(如1),
i变为0,交换到位置0
4. 基准放置:swap(arr, i + 1, high)
- 为什么是
i + 1?- 循环结束后:
i指向最后一个小于等于基准的元素i+1指向第一个大于基准的元素- 将基准与
i+1位置的元素交换,确保: - 基准左边所有元素 ≤ 基准
- 基准右边所有元素 > 基准
- 示例:
- 分区后:
[1, 5, 8, 9, 10, 7](基准是5) i=0(指向1),i+1=1(指向5)- 交换
arr[1]和arr[5](基准5与7交换)→[1, 5, 8, 9, 10, 7](基准在正确位置)
- 分区后:
完整执行流程示例
以数组 [10, 7, 8, 9, 1, 5] 为例:
初始调用:quickSort(arr, 0, 5)
-
第一次分区(partition):
- 基准
pivot = arr[5] = 5 - 初始化
i = -1 - 遍历
j从0到4:- j=0: 10 > 5 → 不操作
- j=1: 7 > 5 → 不操作
- j=2: 8 > 5 → 不操作
- j=3: 9 > 5 → 不操作
- j=4: 1 ≤ 5 → i=0, 交换 arr[0] 和 arr[4] →
[1, 7, 8, 9, 10, 5]
- 遍历结束,交换 arr[i+1](arr[1])和 arr[5] →
[1, 5, 8, 9, 10, 7] - 返回基准位置
1
- 基准
-
递归左半部分:
quickSort(arr, 0, 0)(只有一个元素,直接返回) -
递归右半部分:
quickSort(arr, 2, 5)- 分区:基准
pivot = arr[5] = 7 - 初始化
i = 1 - 遍历
j从2到4:- j=2: 8 > 7 → 不操作
- j=3: 9 > 7 → 不操作
- j=4: 10 > 7 → 不操作
- 遍历结束,交换 arr[i+1](arr[2])和 arr[5] →
[1, 5, 7, 9, 10, 8] - 返回基准位置
2
- 分区:基准
-
递归左半部分:
quickSort(arr, 2, 1)(不满足low<high,返回) -
递归右半部分:
quickSort(arr, 3, 5)- 分区:基准
pivot = arr[5] = 8 - 初始化
i = 2 - 遍历
j从3到4:- j=3: 9 > 8 → 不操作
- j=4: 10 > 8 → 不操作
- 遍历结束,交换 arr[i+1](arr[3])和 arr[5] →
[1, 5, 7, 8, 10, 9] - 返回基准位置
3
- 分区:基准
-
递归左半部分:
quickSort(arr, 3, 2)(返回) -
递归右半部分:
quickSort(arr, 4, 5)- 分区:基准
pivot = arr[5] = 9 - 初始化
i = 3 - 遍历
j从4到4:- j=4: 10 > 9 → 不操作
- 遍历结束,交换 arr[i+1](arr[4])和 arr[5] →
[1, 5, 7, 8, 9, 10] - 返回基准位置
4
- 分区:基准
-
递归左半部分:
quickSort(arr, 4, 3)(返回) -
递归右半部分:
quickSort(arr, 5, 4)(返回)
最终结果:[1, 5, 7, 8, 9, 10]
关键点总结
- 分区操作:快速排序的核心。通过分区将数组分为两部分,并确定基准元素的最终位置。
- 基准选择:本实现选择最右边的元素作为基准。这种选择在数组有序或逆序时会导致最坏情况(时间复杂度O(n²))。实际应用中可采用随机选择或三数取中等方法优化。
- 递归终止条件:
low < high确保至少有两个元素才进行排序。 - 指针
i的作用:维护小于基准的区域的边界,确保所有小于等于基准的元素都被交换到该区域。 - 时间复杂度:
- 平均情况:O(n log n)
- 最坏情况(如数组已有序):O(n²)
- 空间复杂度:O(log n)(递归调用栈的深度)
- 稳定性:不稳定排序(分区过程中可能改变相等元素的相对位置)
快速排序是实际应用中最常用的排序算法之一,因为其平均时间复杂度优秀,且常数因子较小。通过优化基准选择(如随机化)可以避免最坏情况的发生。