快速排序是一种非常高效的排序算法,采用分治策略来对一个数组进行排序。它由C. A. R. Hoare在1960年提出。快速排序的基本思想是通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,然后分别对这两部分继续进行排序,以达到整个序列有序。
一、基本步骤
- 选择基准:从数列中挑出一个元素,称为“基准”(pivot)。
- 分区操作:重新排列数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分割结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
- 递归排序子序列:递归地将小于基准的子序列和大于基准的子序列进行快速排序。
二、Java示例代码
public class QuickSort {
public static void main(String[] args) {
int[] arr = {10, 7, 8, 9, 1, 5};
quickSort(arr, 0, arr.length - 1);
System.out.println("Sorted array: ");
printArray(arr);
}
// 快速排序函数
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// pi是分区操作后基准元素的位置
int pi = partition(arr, low, high);
// 递归地对基准左侧和右侧的子数组进行快速排序
quickSort(arr, low, pi - 1); // 左侧子数组
quickSort(arr, pi + 1, high); // 右侧子数组
}
}
// 分区函数
private static int partition(int[] arr, int low, int high) {
// 选择最右边的元素作为基准
int pivot = arr[high];
// i指向比基准小的最后一个元素
int i = (low - 1); // 最初i位于low-1位置
for (int j = low; j < high; j++) {
// 如果当前元素小于或等于pivot
if (arr[j] <= pivot) {
i++; // 增加i
// 交换arr[i]和arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 交换arr[i+1]和arr[high](或pivot)
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1; // 返回基准元素的新索引
}
// 打印数组
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
}
代码解析
- 主函数 (
main
方法):初始化一个整数数组,并调用quickSort
函数来对其进行排序。之后打印排序后的数组。 - 快速排序方法 (
quickSort
):接收一个数组以及要排序部分的起始和结束索引。如果起始索引小于结束索引,则执行以下操作:- 调用
partition
方法获取新的基准元素位置。 - 对基准元素左边的部分递归调用
quickSort
。 - 对基准元素右边的部分递归调用
quickSort
。
- 调用
- 分区方法 (
partition
):此方法用于将数组分成两部分,并返回基准元素的位置。它使用了两个指针:i
和j
。其中i
指向比基准值小的最后一个元素,而j
则遍历整个数组。每当遇到一个小于或等于基准的元素时,就将i
向右移动一位,并与j
处的元素交换。最后,将基准元素放到正确的位置上。 - 打印数组 (
printArray
):辅助函数,用于打印数组中的所有元素。
三、特点
1. 高效性
- 快速排序在平均情况下的时间复杂度为O(n log n),这使得它成为一种非常高效的排序算法,尤其适用于大数据集。
- 在实际应用中,由于其内部循环可以很好地利用缓存,快速排序往往比其他O(n log n)的排序算法(如归并排序)表现得更好。
2. 原地排序
- 快速排序是一种原地排序算法,即除了递归所需的栈空间外,不需要额外的存储空间。这意味着它的空间复杂度通常为O(log n),这是由于递归调用栈的深度。
3. 不稳定性
- 快速排序是不稳定的排序算法,即相等元素的相对顺序可能在排序后发生变化。这是因为当两个相等的元素被放在基准的两侧时,它们的原始顺序可能会被打乱。
4. 分治法
- 快速排序采用分治策略,通过选择一个基准元素将数组分成两个子数组,然后对这两个子数组分别进行递归排序。这种分而治之的方法使得快速排序能够有效地处理大规模数据集。
5. 最佳、最坏和平均情况
- 最佳情况下,每次划分都能均匀分割数组,时间复杂度为O(n log n)。
- 最坏情况下,如果每次选取的基准都是最小或最大元素,那么每次只能排除一个元素,此时时间复杂度退化为O(n^2)。
- 平均情况下,快速排序的时间复杂度也是O(n log n)。