【问题描述】 编写一个程序,采用不同的排序方法对数据集进行非递减有序排序,并对各种排序方法 的时间性能进行比较。
【实验目的】 掌握各种排序算法的原理、特点和时间性能。
【基本要求】 (1)输入待排序数据集中数据的个数 n (2)产生 n 个随机数 (3)分别用插入排序、选择排序、堆排序、快速排序、希尔排序、冒泡排序、归并排序算法 数据集进行排序,记录下每种排序方法实际消耗时间的毫秒数
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//插入排序
void insertion_sort(int arr[], int n) {
for (int i = 1; i < n; i++) { //外层循环,从数组的第二个元素开始,因为第一个元素默认已经排序
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {//内层循环的条件
arr[j + 1] = arr[j];
j--;//内层循环的结束条件,每次循环都会将j减1,直到找到key应该插入的位置
}
arr[j + 1] = key;
}
}
//选择排序
void selection_sort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {//外层循环,用于遍历整个数组
int min_idx = i;//每次外层循环开始时,初始化一个变量 min_idx,用于记录当前未排序部分的最小值的索引
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[min_idx]) {
min_idx = j;
}
}
int temp = arr[min_idx];
arr[min_idx] = arr[i];//将未排序部分的最小值移动到正确的位置。将当前位置 i 的元素赋值给最小值的位置 min_idx
arr[i] = temp;
}
}
// 堆化函数,将数组调整为最大堆
void heapify(int arr[], int n, int i) {
int largest = i; // 初始化最大值为当前节点
int left = 2 * i + 1; // 左子节点的索引
int right = 2 * i + 2; // 右子节点的索引
// 如果左子节点存在且大于当前最大值,更新最大值
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
// 如果右子节点存在且大于当前最大值,更新最大值
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
// 如果最大值不是当前节点,交换它们并递归地对交换后的节点进行堆化
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
heapify(arr, n, largest);
}
}
// 堆排序
void heap_sort(int arr[], int n) {
// 构建最大堆
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// 一个个从堆顶取出元素(最大值),放到数组末尾,并对剩余元素重新进行堆化
for (int i = n - 1; i >= 0; i--) {
int temp = arr[0]; // 取出堆顶元素
arr[0] = arr[i]; // 将堆顶元素放到数组末尾
arr[i] = temp; // 将原来的末尾元素放到堆顶位置
heapify(arr, i, 0); // 对剩余元素重新进行堆化
}
}
// 分区函数,用于将数组分为两部分,左边的元素都小于基准值,右边的元素都大于基准值
int partition(int arr[], int left, int right) {
// 选择最右边的元素作为基准值
int pivot = arr[right];
// i 指向当前处理的元素的前一个位置
int i = left - 1;
// j 从左到右遍历数组
for (int j = left; j <= right - 1; j++) {
// 如果当前元素小于基准值,则将其与 i+1 位置的元素交换,并将 i 向后移动一位
if (arr[j] < pivot) {
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 将基准值放到正确的位置(即 i+1 的位置)
int temp = arr[i + 1];
arr[i + 1] = arr[right];
arr[right] = temp;
// 返回基准值的位置
return i + 1;
}
// 快速排序函数,递归地将数组分为两部分并排序
void quick_sort(int arr[], int left, int right) {
// 如果左边界小于右边界,说明还有元素需要排序
if (left < right) {
// 调用分区函数,获取基准值的位置
int pivot = partition(arr, left, right);
// 对基准值左边的部分进行排序
quick_sort(arr, left, pivot - 1);
// 对基准值右边的部分进行排序
quick_sort(arr, pivot + 1, right);
}
}
//希尔排序
void shell_sort(int arr[], int n) {
for (int gap = n / 2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j;
for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
arr[j] = arr[j - gap]; // 将当前元素与前面的元素进行比较和交换
}
arr[j] = temp; // 将当前元素插入到正确的位置
}
}
}
//冒泡排序
void bubble_sort(int arr[], int n) {
// 外层循环控制排序趟数
for (int i = 0; i < n - 1; i++) {
// 内层循环控制每一趟排序多少次
for (int j = 0; j < n - i - 1; j++) {
// 如果当前元素大于下一个元素,则交换它们的位置
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 合并两个已排序的数组
void merge(int arr[], int left, int mid, int right) {
int n1 = mid - left + 1; // 左半部分的长度
int n2 = right - mid; // 右半部分的长度
int L[n1], R[n2]; // 创建临时数组
// 将左半部分的元素复制到临时数组L中
for (int i = 0; i < n1; i++) {
L[i] = arr[left + i];
}
// 将右半部分的元素复制到临时数组R中
for (int j = 0; j < n2; j++) {
R[j] = arr[mid + 1 + j];
}
int i = 0, j = 0, k = left; // 初始化三个指针
// 比较临时数组L和R的元素,将较小的元素复制回原数组
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
// 如果临时数组L还有剩余元素,将其复制回原数组
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
// 如果临时数组R还有剩余元素,将其复制回原数组
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
// 归并排序
void merge_sort(int arr[], int left, int right) {
// 如果数组长度大于1,那么对其进行排序
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); // 将两部分合并
}
}
int main() {
int n;
printf("请输入待排序数据集中数据的个数: \n");
scanf("%d", &n);
int *arr = (int *)malloc(n * sizeof(int));
srand(time(NULL));
for (int i = 0; i < n; i++) {
arr[i] = rand() % n;//生成任意随机数
}
printf("原始数组: \n");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
int j=0;
while(j<10){
int choice;
printf("请选择排序方式: \n");
printf("1. 插入排序\n");
printf("2. 选择排序\n");
printf("3. 堆排序\n");
printf("4. 快速排序\n");
printf("5. 希尔排序\n");
printf("6. 冒泡排序\n");
printf("7. 归并排序\n");
scanf("%d", &choice);
clock_t start, end;
double cpu_time_used;
switch (choice) {
case 1:
start = clock();
insertion_sort(arr, n);
end = clock();
break;
case 2:
start = clock();
selection_sort(arr, n);
end = clock();
break;
case 3:
start = clock();
heap_sort(arr, n);
end = clock();
break;
case 4:
start = clock();
quick_sort(arr, 0, n - 1);
end = clock();
break;
case 5:
start = clock();
shell_sort(arr, n);
end = clock();
break;
case 6:
start = clock();
bubble_sort(arr, n);
end = clock();
break;
case 7:
start = clock();
merge_sort(arr, 0, n - 1);
end = clock();
break;
default:
printf("无效的选择\n");
return 1;
}
printf("排序后的数组: \n");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
cpu_time_used = cpu_time_used*1000;
printf("排序方法实际消耗时间的毫秒数: %f ms\n", cpu_time_used );
j++;
}
free(arr);
return 0;
}