快排
题目:
快速排序。
分析:
使用递归分治的方法,递归定义如下:
T
(
n
)
=
{
1
,
n
=
1
T
(
n
−
1
)
+
O
(
n
)
,
n
>
1
T(n) = \begin{cases} 1, \quad \quad \quad \quad \quad \quad \quad \space \space n=1 \\ T(n-1) + O(n), \quad n>1 \end{cases}
T(n)={1, n=1T(n−1)+O(n),n>1
平均时间复杂度:O(nlogn)
最坏时间复杂度:O(n2)
稳定性:不稳定
实现一:数据结构中的方法(王道)
1. **思想**
选择一个 pivot
基准点(左端点),分别从右边和左边靠近。先从大的一端开始(右边),当右边小于 list[pivot]
时,将右边赋值给左边 list[left] = list[right]
;接着从左边靠近。当左边大于 list[pivot]
时,将左边赋值给右边 list[right] = list[left]
。往复循环,直至相遇。相遇点 left
即为 piovt
最终位置,将pivot放置在 list[left]
,形成左边都小于等于 list[pivot]
,右边都大于等于 list[pivot]
。从 pivot
处分成左右两块 (left, pivot - 1)
(pivot + 1, right)
进行上述相同操作,直至分块长度小于1时,完成排序。
2. **代码**
#include <stdio.h>
/*
稳定性:不稳定
递归定义:
{ 1 n=1
T(n)={
{ T(n-1) + O(n) n>1
平均时间复杂度:O(nlogn)
最坏时间复杂度:O(n2)
*/
// 分区(数据结构中写法)
int partitionStruct(int list[], int left, int right) {
int pivot = list[left];
while (left < right) {
while (left < right && list[right] >= pivot)
right--;
list[left] = list[right];
while (left < right && list[left] <= pivot)
left++;
list[right] = list[left];
}
list[left] = pivot;
return left;
}
// 快排
void quickSort(int list[], int left, int right) {
if (left >= right) {
return;
}
int pivot = partitionStruct(list, left, right);
// 左边
quickSort(list, left, pivot - 1);
// 右边
quickSort(list, pivot + 1, right);
}
void main() {
int list[] = {2, 1, 7, 3, 5, 9};
int length = sizeof(list) / sizeof(int);
int i;
quickSort(list, 0, length - 1);
printf("规律分区\n");
for (i = 0; i < length; i++) {
printf("%d ", list[i]);
}
}
实现二:算法设计与分析中的方法(王晓东-第五版)
- 思想
选择一个 pivot
基准点(左端点),基准点索引为 p
分别从右边和左边靠近。先从大的一端开始(右边),当右边小于 list[pivot]
时,接着从左边靠近。当左边大于 list[pivot]
时,结束一轮循环,交换左右元素 swap(list, left, right)
。往复循环,直至相遇。将基准点和left位置的元素进行交换 swap(list, p, left)
,形成左边都小于等于 list[p]
,右边都大于等于 list[p]
。从pivot处分成左右两块 (left, pivot - 1)
(pivot + 1, right)
进行上述相同操作,直至分块长度小于1时,完成排序。
- 代码
#include <stdio.h>
/*
稳定性:不稳定
递归定义:
{ 1 n=1
T(n)={
{ T(n-1) + O(n) n>1
平均时间复杂度:O(nlogn)
最坏时间复杂度:O(n2)
*/
// 交换
void swap(int list[], int left, int right) {
int temp = list[right];
list[right] = list[left];
list[left] = temp;
}
// 分区(算法书中写法)
int partitionAlgorithm(int list[], int left, int right) {
int pivot = list[left];
int p = left;
while (left < right) {
while (left < right && list[right] >= pivot)
right--;
while (left < right && list[left] <= pivot)
left++;
swap(list, left, right);
}
swap(list, p, left);
return right;
}
// 快排
void quickSort(int list[], int left, int right) {
if (left >= right) {
return;
}
int pivot = partitionAlgorithm(list, left, right);
// 左边
quickSort(list, left, pivot - 1);
// 右边
quickSort(list, pivot + 1, right);
}
void main() {
int list[] = {2, 1, 7, 3, 5, 9};
int length = sizeof(list) / sizeof(int);
int i;
quickSort(list, 0, length - 1);
printf("规律分区\n");
for (i = 0; i < length; i++) {
printf("%d ", list[i]);
}
}
实现三:随机分区
- 思想
由于在极端情况下,选择最左端的位置元素为基准元素,可能会导致快排退化为交换排序,时间复杂度变为 O(n2)
。快速排序算法的性能取决于划分的对称性。通过修改 partition
策略,采用随机选择策略,在 left
和 right
之间随机选出一个元素作为划分基准,减少极端情况的出现。
- 代码
#include <stdio.h>
/*
稳定性:不稳定
递归定义:
{ 1 n=1
T(n)={
{ T(n-1) + O(n) n>1
平均时间复杂度:O(nlogn)
最坏时间复杂度:O(n2)
*/
// 交换
void swap(int list[], int left, int right) {
int temp = list[right];
list[right] = list[left];
list[left] = temp;
}
// 分区(算法书中写法)
int partitionAlgorithm(int list[], int left, int right) {
int pivot = list[left];
int p = left;
while (left < right) {
while (left < right && list[right] >= pivot)
right--;
while (left < right && list[left] <= pivot)
left++;
swap(list, left, right);
}
swap(list, p, left);
return right;
}
// 随机分区
int randomPartition(int list[], int left, int right) {
int r = rand() % (right - left) + left;
swap(list, r, left);
return partitionAlgorithm(list, left, right);
}
// 随机分区的快排
void quickSortR(int list[], int left, int right) {
if (left < right) {
int pivot = randomPartition(list, left, right);
// 左边
quickSortR(list, left, pivot - 1);
// 右边
quickSortR(list, pivot + 1, right);
}
}
void main() {
int i;
int listR[] = {2, 1, 7, 3, 5, 9};
int length = sizeof(listR) / sizeof(int);
quickSortR(listR, 0, length - 1);
printf("\n随机分区\n");
for (i = 0; i < length; i++) {
printf("%d ", listR[i]);
}
}