快速排序(Quick Sort)是一种常用的排序算法,它的核心思想是通过分治法(Divide and Conquer)来实现。它的步骤相对简单,但是能够高效地对数据进行排序。
基本思想
快速排序的基本思想可以概括为:
- 分解:选取一个基准元素(pivot),将数组分成两个子数组,左边的子数组中的元素都比基准元素小,右边的子数组中的元素都比基准元素大。
- 递归排序:对这两个子数组分别递归地应用快速排序算法。
- 合并:将排好序的子数组合并起来,得到最终的排序数组。
具体步骤
-
选择基准元素:从数组中选择一个元素作为基准(pivot)。通常选择第一个元素、最后一个元素或者随机选择。
-
分区过程:通过一趟排序将待排序的数组分成独立的两部分,其中一部分的所有元素都比基准元素小,另一部分所有元素都比基准元素大。这个过程称为分区(partition)操作。
- 设置两个指针,左指针指向数组的起始位置,右指针指向数组的末尾。
- 移动左指针直到找到一个大于等于基准元素的元素,移动右指针直到找到一个小于等于基准元素的元素,然后交换这两个元素。
- 重复这个过程,直到左指针超过右指针。这时,右指针所在位置就是基准元素的最终位置,将数组分成了两个部分。
-
递归排序:对分区后的两个子数组递归地应用上述步骤,直到整个数组变成有序。
-
合并结果:当递归处理完所有子数组后,整个数组就变成了有序的。
示例
假设要对数组 [5, 2, 9, 3, 7, 6, 1, 8] 进行快速排序:
- 选择
pivot = 5(通常选择第一个元素作为基准)。 - 进行分区操作,最终数组可能变成
[3, 2, 1, 5, 7, 6, 9, 8],此时pivot的位置在第四个元素5处。 - 递归对左右两个子数组
[3, 2, 1]和[7, 6, 9, 8]进行同样的操作,直到整个数组有序。
时间复杂度
快速排序的平均时间复杂度为 O(n log n),最坏情况下为 O(n^2)(如数组已经有序或基本有序的情况),空间复杂度为 O(log n)(递归调用的栈空间)。
/**
* 交换排序-快速排序,O(n²),不稳定
*
* @param num
* @param l 数组起始索引位置
* @param r 数组末尾索引位置
* @return
*/
public static int[] quickSort(int[] num, int l, int r) {
//r为数组元素总个数,last下标等于r-1
int first = l, last = r , key = num[first];
while (first < last) {
// 从右侧开始,跳过比基点大的元素,大的元素无需移动
while (first < last && num[last] >= key) {
last--;
}
//如果值小于 key分界值 交换
num[first] = num[last];
// 从左侧开始,跳过比基准小的元素,小的元素无需移动
while (first < last && num[first] < key) {
first++;
}
//如果值大于key分界值 交换
num[last] = num[first];
}
// 左右重合,找到基点位置
num[first] = key;
//递归左右部分进行快排
if (first > l) {
num = quickSort(num, l, first);
}
if (first + 1 < r) {
num = quickSort(num, first + 1, r);
}
return num;
}
总结
快速排序是一种高效的排序算法,对于大部分数据集合,它的性能非常优秀。理解快速排序的核心在于理解分区过程和递归调用的思想,通过不断分割数组和合并结果,实现整体数组的排序。