一,概念及其介绍
快速排序由 C. A. R. Hoare 在 1960 年提出。
随机化快速排序基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
二,复杂度说明
时间复杂度: O(n log n)
随机化快速排序的平均时间复杂度为 O(nlog n),最坏情况下的时间复杂度为 O(n^2),但这种最坏情况出现的概率极低。
在平均情况下,每次划分都能将数组大致平均分成两部分,使得递归的深度为 log n 级别。每次划分操作的时间复杂度为 O(n),所以总的平均时间复杂度为 O(nlog n)。
然而,在最坏情况下,例如数组已经有序,每次划分都选择到了最小或最大的元素作为基准,导致划分极不均匀,递归深度达到 $n$ 级别,时间复杂度就变为 O(n^2)。
空间复杂度:O(log n)
空间复杂度方面,随机化快速排序的空间复杂度主要取决于递归调用栈的空间。在平均情况下,空间复杂度为 O(log n),用于存储递归过程中的函数调用信息。
在最坏情况下,空间复杂度为 O(n)。
三,过程图示
四.代码及解析
4.1 python
思路:
先定义一个函数,在传入的数组中随机选一个数,将比它小的元素放到他左边,比他大的放到右边,实现方式就是从列表首和尾分别开始遍历,直到左边找到一个比选定分割数大的数,右边找到一个比它小的,交换两个数的位置直到分区完成返回选定的分区数
将整个数组传入后分区,再对返回的分区数两边分别分区,用函数递归调用实现,直到子数组只有一个元素
知识点:
这段代码第一个函数是传入一个列表,在函数中直接对列表进行操作,每次返回的是分区所随机选定的中间数,第二个函数是利用函数递归,将列表不断分为两段,递归进行操作
import random
def partition(arr, low, high):
# 随机选一个数作为分割点
pivot = arr[random.randint(low, high)]
i = low - 1
j = high + 1
while True:
i += 1
# 从左边找到一个比pivot大的数
while arr[i] < pivot:
i += 1
# 从右边找到一个比pivot小的数
j -= 1
while arr[j] > pivot:
j -= 1
# 分区已经完成
if i >= j:
return j
# 交换位置
arr[i], arr[j] = arr[j], arr[i]
def quick_sort_randomized(arr, low, high):
# 判断是否还有需要排序的子数组(即子数组至少包含两个元素)
if low < high:
pi = partition(arr, low, high)
quick_sort_randomized(arr, low, pi) # 左半部分子数组(从 low 到 pi )进行递归排序
quick_sort_randomized(arr, pi + 1, high)
# 测试示例
arr = [12, 11, 13, 5, 6]
n = len(arr)
quick_sort_randomized(arr, 0, n - 1)
print("排序后的数组:", arr)
排序后的数组: [5, 6, 11, 12, 13]
4.2 C
思路:
分区函数选择数组最后一个元素作为分区轴,然后遍历从 low
到 high - 1
的元素。
如果当前元素小于或等于枢轴,就将 i
递增,并交换 arr[i]
和 arr[j]
的位置,这样小于枢轴的元素就逐渐被移到了左边。最后,将枢轴与 arr[i + 1]
交换位置,使得枢轴处于正确的位置,即左边的元素都小于枢轴,右边的元素都大于枢轴,并返回枢轴的新位置 i + 1
quickSort
函数:首先检查当前的子数组是否至少有两个元素(low < high
)。如果是,调用 partition
函数对当前子数组进行分区,得到分区点的索引 pi
。然后对左半部分子数组(从 low
到 pi - 1
)和右半部分子数组(从 pi + 1
到 high
)分别进行递归调用 quickSort
函数,以完成整个数组的排序。通过不断地分区和递归,最终将数组排序
这里还需要额外定义一个函数用来交换两个元素在数组中的位置。
注意点:
1.交换函数中传入的是两个指针,通过修改指针所指向的内存地址的元素值来进行交换元素
2.分区函数中定义i=low-1后,交换i和j的元素之前要先将i++
3.最后i遍历过的元素都是比轴小,交换过来的,所以要把轴放到i+1的位置
4.求数组长度可以用int n = sizeof(arr) / sizeof(arr[0]);这样的格式
5.i
变量:
它被初始化为 low - 1
。其作用是标记小于或等于枢轴元素的边界位置。在遍历过程中,每当遇到小于或等于枢轴的元素,就将 i
递增,并将该元素与 arr[i]
交换位置,从而将小于或等于枢轴的元素逐渐移到数组的左侧。
j
变量:
它从 low
开始,逐步递增到 high - 1
。用于逐个检查数组中的元素,与枢轴进行比较,并根据比较结果决定是否与 arr[i]
交换位置
#include <stdio.h>
// 交换函数
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// 分区函数
int partition(int arr[], int low, int high) {
int pivot = arr[high];
int i = (low - 1);
for (int j = low; j <= high - 1; j++) {
if (arr[j] <= pivot) {
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]);
return (i + 1);
}
// 快速排序函数
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
// 打印数组函数
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
// 测试示例
int main() {
int arr[] = {12, 11, 13, 5, 6};
int n = sizeof(arr) / sizeof(arr[0]);
printf("排序前的数组为: ");
printArray(arr, n);
quickSort(arr, 0, n - 1);
printf("排序后的数组为: ");
printArray(arr, n);
return 0;
}
4.3 C++
思路:
partition
函数:
选择数组的最后一个元素作为枢轴。通过遍历,将小于等于枢轴的元素移动到数组的左边,大于枢轴的元素移动到数组的右边。最终将枢轴放置在正确的位置,并返回该位置的索引。
quickSort
函数:
如果待排序的子数组至少包含两个元素,就进行分区操作。根据分区得到的枢轴位置,对左子数组和右子数组分别递归调用 quickSort
函数。这样不断递归地对子数组进行排序,最终使整个数组有序
注意点:
1.交换函数传入两个引用,引用是变量的别名,通过修改引用可以修改变量的值 ,从而达到交换的目的
2.计算数组长度 int n = sizeof(arr) / sizeof(arr[0]);
3.i
变量:
它被初始化为 low - 1
。其作用是标记小于或等于枢轴元素的边界位置。在遍历过程中,每当遇到小于或等于枢轴的元素,就将 i
递增,并将该元素与 arr[i]
交换位置,从而将小于或等于枢轴的元素逐渐移到数组的左侧。
j
变量:
它从 low
开始,逐步递增到 high - 1
。用于逐个检查数组中的元素,与枢轴进行比较,并根据比较结果决定是否与 arr[i]
交换位置
#include <iostream>
// 交换函数
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// 分区函数
int partition(int arr[], int low, int high) {
int pivot = arr[high];
int i = (low - 1);
for (int j = low; j <= high - 1; j++) {
if (arr[j] <= pivot) {
i++;
swap(arr[i], arr[j]);
}
}
swap(arr[i + 1], arr[high]);
return (i + 1);
}
// 快速排序函数
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
// 打印数组函数
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++)
std::cout << arr[i] << " ";
std::cout << std::endl;
}
// 测试示例
int main() {
int arr[] = {12, 11, 13, 5, 6};
int n = sizeof(arr) / sizeof(arr[0]);
std::cout << "排序前的数组为: ";
printArray(arr, n);
quickSort(arr, 0, n - 1);
std::cout << "排序后的数组为: ";
printArray(arr, n);
return 0;
}