#include <stdio.h> // 引入标准输入输出头文件
#include <stdlib.h> // 引入标准库头文件
#include <windows.h> // 引入Windows平台头文件
#include <psapi.h>
// 给定生成随机数的个数,可修改
#define TEST_NUM 10 // 定义一个常量,表示生成随机数的个数
// ANSI 转义序列,用于改变文本颜色
#define ANSI_COLOR_GREEN "\x1b[32m" // 定义绿色的ANSI转义序列
#define ANSI_COLOR_RESET "\x1b[0m" // 定义重置颜色的ANSI转义序列
int quicksort_count = 0; // 定义一个全局变量用于记录快速排序迭代次数
// 定义一个函数来获取当前时间戳(微秒级别)
double get_time() {
LARGE_INTEGER frequency; // 定义一个大整数结构体用于存储高精度计数器的频率
LARGE_INTEGER start_time; // 定义一个大整数结构体用于存储开始时间
QueryPerformanceFrequency(&frequency); // 获取高精度计数器的频率
QueryPerformanceCounter(&start_time); // 获取开始时间
return (double)start_time.QuadPart / (double)frequency.QuadPart; // 返回当前时间戳(微秒级别)
}
// 快速排序算法
void quicksort(int arr[], int left, int right, int size) {
if (left < right) {
int pivot = arr[left]; // 设置基准元素为数组的第一个元素
int i = left; // 定义左指针
int j = right + 1; // 定义右指针
while (1) {
do {
i++;
} while (arr[i] < pivot); // 从左向右找到第一个大于等于基准元素的位置
do {
j--;
} while (arr[j] > pivot); // 从右向左找到第一个小于等于基准元素的位置
if (i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp; // 交换左右两个元素的位置
}
else {
break;
}
}
int temp = arr[left];
arr[left] = arr[j];
arr[j] = temp; // 将基准元素放到正确的位置
// 打印当前迭代后的数组顺序,并高亮打印交换的两个数
printf("迭代 %d 次后的数组顺序: ", quicksort_count++);
for (int k = 0; k < size; k++) {
if (k == left || k == j) {
printf(ANSI_COLOR_GREEN "%d " ANSI_COLOR_RESET, arr[k]);
}
else {
printf("%d ", arr[k]);
}
}
printf("\n");
quicksort(arr, left, j - 1, size); // 递归处理左边的子数组
quicksort(arr, j + 1, right, size); // 递归处理右边的子数组
}
}
// 归并排序算法
void merge(int arr[], int left, int mid, int right) {
int i, j, k;
int n1 = mid - left + 1; // 计算左子数组的大小
int n2 = right - mid; // 计算右子数组的大小
int* L = (int*)malloc(n1 * sizeof(int)); // 为左子数组分配内存
int* R = (int*)malloc(n2 * sizeof(int)); // 为右子数组分配内存
for (i = 0; i < n1; i++) {
L[i] = arr[left + i]; // 复制左半部分到临时数组L
}
for (j = 0; j < n2; j++) {
R[j] = arr[mid + 1 + j]; // 复制右半部分到临时数组R
}
i = 0;
j = 0;
k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
}
else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
free(L); // 释放临时数组L的内存
free(R); // 释放临时数组R的内存
for (int k = left; k < right; k++) {
printf("%d ", arr[k]);
}
printf("\n");
}
void merge_sort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
merge_sort(arr, left, mid); // 递归处理左半部分
merge_sort(arr, mid + 1, right); // 递归处理右半部分
merge(arr, left, mid, right); // 合并左右两部分
}
}
// 选择排序算法
void selection_sort(int arr[], int n) {
int i, j, min_idx;
for (i = 0; i < n - 1; i++) {
min_idx = i;
for (j = i + 1; j < n; j++) {
if (arr[j] < arr[min_idx]) {
min_idx = j;
}
}
int temp = arr[i];
arr[i] = arr[min_idx];
arr[min_idx] = temp; // 将当前位置的元素与最小元素进行交换
// 打印当前迭代后的数组顺序,并高亮打印交换的两个数
printf("迭代 %d 次后的数组顺序: ", i + 1);
for (int k = 0; k < n; k++) {
if (k == i || k == min_idx) {
printf(ANSI_COLOR_GREEN "%d " ANSI_COLOR_RESET, arr[k]);
}
else {
printf("%d ", arr[k]);
}
}
printf("\n");
}
}
// 填充随机测试数组
void random_add(int arr1[], int arr2[], int arr3[], int size) {
for (int i = 0; i < size; i++) {
arr1[i] = rand() % 1000000000; // 随机数范围0 ≤ x < 10^9
arr2[i] = arr1[i]; // 复制到arr2
arr3[i] = arr1[i]; // 复制到arr3
}
for (int i = 0; i < size; i++) {
printf("%d ", arr1[i]); // 打印随机数
}
}
//定义printMemoryInfo函数来打印内存使用信息
void printMemoryInfo(HANDLE process) {
PROCESS_MEMORY_COUNTERS_EX pmc;
if (GetProcessMemoryInfo(process, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
printf("私有工作集: %llu 字节\n", (ULONGLONG)pmc.PrivateUsage);
printf("共享工作集: %llu 字节\n", (ULONGLONG)(pmc.WorkingSetSize - pmc.PrivateUsage));
printf("虚拟内存大小: %llu 字节\n", (ULONGLONG)pmc.PagefileUsage);
}
}
int main() {
int arr1[TEST_NUM];
int arr2[TEST_NUM];
int arr3[TEST_NUM];
int size = sizeof(arr1) / sizeof(arr1[0]);
printf("随机生成的数组如下:\n");
random_add(arr1, arr2, arr3, size);
// 快速排序
printf("\n快速排序:\n");
double start, end;
start = get_time();
// 在排序之前获取和打印内存使用情况
/*printf("排序前:\n");
PROCESS_MEMORY_COUNTERS_EX pmc;
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
printf("私有工作集: %llu 字节\n", (ULONGLONG)pmc.PrivateUsage);
printf("共享工作集: %llu 字节\n", (ULONGLONG)(pmc.WorkingSetSize - pmc.PrivateUsage));
printf("虚拟内存大小: %llu 字节\n", (ULONGLONG)pmc.PagefileUsage);
}*/
quicksort(arr1, 0, size - 1, size);
// 在排序之后获取和打印内存使用情况
/*printf("排序后:\n");
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
printf("私有工作集: %llu 字节\n", (ULONGLONG)pmc.PrivateUsage);
printf("共享工作集: %llu 字节\n", (ULONGLONG)(pmc.WorkingSetSize - pmc.PrivateUsage));
printf("虚拟内存大小: %llu 字节\n", (ULONGLONG)pmc.PagefileUsage);
}*/
end = get_time();
printf("运行时间: %lf seconds\n", (double)(end - start));
// 归并排序
printf("\n归并排序:\n");
start = get_time();
///在排序之前获取和打印内存使用情况
/*printf("排序前:\n");
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
printf("私有工作集: %llu 字节\n", (ULONGLONG)pmc.PrivateUsage);
printf("共享工作集: %llu 字节\n", (ULONGLONG)(pmc.WorkingSetSize - pmc.PrivateUsage));
printf("虚拟内存大小: %llu 字节\n", (ULONGLONG)pmc.PagefileUsage);
}*/
merge_sort(arr2, 0, size - 1);
// 在排序之后获取和打印内存使用情况
/*printf("排序后:\n");
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
printf("私有工作集: %llu 字节\n", (ULONGLONG)pmc.PrivateUsage);
printf("共享工作集: %llu 字节\n", (ULONGLONG)(pmc.WorkingSetSize - pmc.PrivateUsage));
printf("虚拟内存大小: %llu 字节\n", (ULONGLONG)pmc.PagefileUsage);
}*/
end = get_time();
printf("运行时间: %lf seconds\n", (double)(end - start));
// 选择排序
printf("\n选择排序:\n");
start = get_time();
///在排序之前获取和打印内存使用情况
/*printf("排序前:\n");
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
printf("私有工作集: %llu 字节\n", (ULONGLONG)pmc.PrivateUsage);
printf("共享工作集: %llu 字节\n", (ULONGLONG)(pmc.WorkingSetSize - pmc.PrivateUsage));
printf("虚拟内存大小: %llu 字节\n", (ULONGLONG)pmc.PagefileUsage);
}*/
selection_sort(arr3, size);
// 在排序之后获取和打印内存使用情况
/*printf("排序后:\n");
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
printf("私有工作集: %llu 字节\n", (ULONGLONG)pmc.PrivateUsage);
printf("共享工作集: %llu 字节\n", (ULONGLONG)(pmc.WorkingSetSize - pmc.PrivateUsage));
printf("虚拟内存大小: %llu 字节\n", (ULONGLONG)pmc.PagefileUsage);
}*/
end = get_time();
printf("运行时间: %lf seconds\n", (double)(end - start));
return 0;
}
- 输入随机生成0≦x≦109数组,长度分别为5、10、100;
5:
10:
100:
- 时间复杂度、空间复杂度、实际执行时间、实际占用内存空间
快速排序(Quick Sort):
时间复杂度:
最优情况:O(n log n)
平均情况:O(n log n)
最坏情况:O(n^2)(当数组已经有序时)
空间复杂度:
最优情况:O(log n)(递归调用栈的深度)
最坏情况:O(n)(当数组已经有序时)
归并排序(Merge Sort):
时间复杂度:
最优情况:O(n log n)
平均情况:O(n log n)
最坏情况:O(n log n)
空间复杂度:
总体空间复杂度为O(n),但是因为归并排序是基于分治法的,所以它需要额外的空间来存储临时数组,因此空间复杂度为O(n)。
选择排序(Selection Sort):
时间复杂度:
最优情况:O(n^2)
平均情况:O(n^2)
最坏情况:O(n^2)
空间复杂度:
O(1)(不需要额外的空间,只需要交换元素的位置)
实际执行时间、实际占用内存空间以10长度为例:
- 每次程序迭代后的数组顺序
研究与思考:分析算法时间复杂性,能否将递归程序非递归化?
快速排序的非递归版本:
时间复杂度:
最优情况:O(n log n)
平均情况:O(n log n)
最坏情况:O(n^2)(当数组已经有序时)
空间复杂度:
最优情况:O(log n)(栈空间用于存储分割点)
最坏情况:O(n)(当数组已经有序时)
归并排序的非递归版本:
时间复杂度:
最优情况:O(n log n)
平均情况:O(n log n)
最坏情况:O(n log n)
空间复杂度:
总体空间复杂度为O(n),但因为归并排序需要额外的空间来存储临时数组,所以空间复杂度为O(n)。
选择排序:
选择排序本身就是一个迭代算法,不需要进行递归化。其时间复杂度和空间复杂度如下:
时间复杂度:
最优情况:O(n^2)
平均情况:O(n^2)
最坏情况:O(n^2)
空间复杂度:
O(1)(不需要额外的空间,只需要交换元素的位置)