目录
本文介绍了快速排序算法的原理和实现方法,并通过C++代码展示了具体实现。快速排序的基本思想是分治法,即通过递归将一个大问题划分成多个小问题来实现排序。在实现过程中,需要选择合适的基准元素,可以使用随机化或者三数取中等方式来避免最坏情况的出现,并且可以使用插入排序等优化手段来提高排序性能。快速排序的时间复杂度为O(n log n),而在最坏情况下可能会退化为O(n^2)。快速排序是一种非常实用和高效的排序算法,被广泛应用于各种软件开发和计算机科学领域。
概要
快速排序是一种高效的排序算法,它的时间复杂度为Θ(n log n)。快排的核心思想是分治法,通过将一个大问题分解成多个小问题来实现排序。下面我们来介绍如何使用 C++ 实现快速排序。
快速排序是一种高效的排序算法,它的时间复杂度为Θ(n log n),它的基本思想是采用分治法将待排序的元素序列划分成两部分,其中一部分的元素都比另一部分的元素小,然后再按照同样的方法对两部分进行排序,直到整个序列有序。以下是使用 C++ 实现快速排序的详细步骤:
-
选取一个枢轴元素:从待排序序列中选择一个元素作为枢轴元素,一般选择第一个元素或者最后一个元素。
-
将序列分成两个部分:将待排序序列中小于枢轴元素的放到左边,大于枢轴元素的放到右边。这个过程叫做划分过程。
-
对左右两部分分别进行快速排序:递归地对划分后得到的左右两部分分别进行排序,直到整个序列有序。
void quickSort(int arr[], int left, int right) {
if (left >= right) return; // 数组长度为1时已经有序
int pivot = arr[left]; // 以数组第一个元素作为枢轴元素
int i = left, j = right;
while (i < j) {
// 从右往左找第一个小于pivot的元素
while (i < j && arr[j] >= pivot) j--;
arr[i] = arr[j];
// 从左往右找第一个大于pivot的元素
while (i < j && arr[i] <= pivot) i++;
arr[j] = arr[i];
}
arr[i] = pivot; // 将枢轴元素放到正确的位置
// 递归地对左右两部分进行排序
quickSort(arr, left, i - 1);
quickSort(arr, i + 1, right);
}
一、快速排序的原理
快速排序的基本思想是:从待排序的序列中选出一个基准元素(通常选择第一个元素),然后将序列划分成两个子序列,一个子序列所有元素小于或等于基准元素,另一个子序列所有元素大于基准元素,最后递归地对两个子序列进行排序。当子序列中只有一个元素时,排序完成。
二、C++ 实现快速排序
下面是 C++ 实现快速排序的代码:
void quickSort(int arr[], int left, int right) {
int i = left, j = right;
int pivot = arr[(left + right) / 2];
while (i <= j) {
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
swap(arr[i], arr[j]);
i++;
j--;
}
}
if (left < j)
quickSort(arr, left, j);
if (i < right)
quickSort(arr, i, right);
}
三、快速排序的工作原理
快速排序的核心是 partition 函数,它将序列划分成两个子序列。在上面的实现中,我们使用左右指针 i 和 j 分别指向序列的左右两端,然后从两端开始扫描序列,如果左指针指向的元素小于基准元素,则向右移动左指针;如果右指针指向的元素大于基准元素,则向左移动右指针;如果左指针指向的元素大于等于基准元素且右指针指向的元素小于等于基准元素,则交换这两个元素并同时向中间移动。
上面的代码中,我们使用 arr[(left + right) / 2] 作为基准元素,也可以选择其他元素作为基准元素,比如第一个、最后一个或中间的元素。
四、快速排序的时间复杂度
在最坏情况下,快速排序的时间复杂度是 Θ(n^2),即当序列已经有序或接近有序时,分治法会失效,此时需要进行 n-1 次比较,排序的时间复杂度退化为 O(n^2)。在平均情况下,快速排序的时间复杂度是 Θ(n log n)。在最好情况下,即每次 pivot 都恰好为中位数的情况下,时间复杂度是 O(n log n)。
五、快速排序的优化
为了提高快速排序的性能,我们可以通过以下方式对其进行优化:
- 随机选择基准元素:可以避免最坏情况的出现;
- 三数取中法:在 left、mid 和 right 三个位置上取一个数来作为基准元素,可以减少最坏情况的出现;
- 插入排序:当序列长度小于一定阈值时,可以使用插入排序代替快速排序;
- 尾递归消除:可以优化递归过程,避免栈溢出的风险。
对快速排序进行优化可以提高其性能,下面是一个经过优化的快速排序实现:
// 三数取中法选择基准元素
int median3(int arr[], int left, int right) {
int center = (left + right) / 2;
if (arr[left] > arr[center])
swap(arr[left], arr[center]);
if (arr[left] > arr[right])
swap(arr[left], arr[right]);
if (arr[center] > arr[right])
swap(arr[center], arr[right]);
swap(arr[center], arr[right - 1]); // 将基准元素放到倒数第二个位置
return arr[right - 1];
}
// 插入排序
void insertionSort(int arr[], int left, int right) {
for (int i = left + 1; i <= right; i++) {
int temp = arr[i];
int j = i - 1;
while (j >= left && arr[j] > temp) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
}
// 递归快速排序
void quickSort(int arr[], int left, int right) {
const int THRESHOLD = 10; // 阈值
if (right - left >= THRESHOLD) {
int pivot = median3(arr, left, right); // 选择基准元素
int i = left, j = right - 1;
while (true) {
while (arr[++i] < pivot) {}
while (arr[--j] > pivot) {}
if (i < j)
swap(arr[i], arr[j]);
else
break;
}
swap(arr[i], arr[right - 1]); // 将基准元素放到正确的位置
quickSort(arr, left, i - 1); // 递归排序左子序列
quickSort(arr, i + 1, right); // 递归排序右子序列
} else {
insertionSort(arr, left, right); // 使用插入排序对小序列排序
}
}
上述代码中,我们使用了三数取中法来选择基准元素,在选定了基准元素之后,我们将其放到倒数第二个位置,这是为了方便处理右边界的情况。在递归排序左右子序列之前,我们先判断序列长度是否小于一个阈值(这里设置为 10),如果小于等于阈值,则使用插入排序代替快速排序。通过上述优化,快速排序的性能得到了提升。
六、总结
快速排序是一种高效的排序算法,其时间复杂度为 Θ(n log n)。在实现快速排序时,需要注意选择合适的基准元素,并对其进行随机化或者三数取中来避免最坏情况的出现。同时可以使用插入排序等优化方法来提高排序性能。