内部排序算法比较

在教科书中,各种内部排序算法的时间复杂度分析结果只给出了算法执行时间的阶,或大概执行时间。试通过随机数据比较各算法的关键字比较次数和关键字移动次数,以取得直观感受。

基本要求

  • 对以下6种常用的内部排序算法进行比较冒泡排序、直接插入排序、简单选择排序、快速排序、希尔排序、堆排序。
  • 待排序表的表长不小于1005其中的数据要用伪随机数产生程序产生:至少要用5组不同的输入数据作比较:比较的指标为有关键字参加的比较次数和关键字的移动次数(关键字交换计为3次移动)。
  • 最后要对结果作出简单分析,包括对各组数据得出结果波动大小的解释。

测试数据

由随机数产生器生成

实现提示

主要工作是设法在已知算法中的适当位置插入对关键字的比较次数和移动次数的计数操作。程序还可以考虑几组数据的典型性,如,正序、逆序和不同程度的乱序。注意采用分块调试的方法。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SIZE 1005

// 生成伪随机数的函数
void generateRandomArray(int array[], int size) {
srand(time(NULL));
for (int i = 0; i < size; i++) {
array[i] = rand() % 10000; // 生成介于 0 和 9999 之间的随机数
}
}

// 交换函数
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}

// 冒泡排序
void bubbleSort(int array[], int size, int* compCount, int* moveCount) {
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
(*compCount)++;
if (array[j] > array[j + 1]) {
(*moveCount) += 3; // 交换需要三步
swap(&array[j], &array[j + 1]);
}
}
}
}

// 插入排序
void insertionSort(int array[], int size, int* compCount, int* moveCount) {
for (int i = 1; i < size; i++) {
int key = array[i];
int j = i - 1;
while (j >= 0 && array[j] > key) {
(*compCount)++;
array[j + 1] = array[j];
(*moveCount)++;
j--;
}
array[j + 1] = key;
(*moveCount)++;
}
}

// 选择排序
void selectionSort(int array[], int size, int* compCount, int* moveCount) {
for (int i = 0; i < size - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < size; j++) {
(*compCount)++;
if (array[j] < array[minIndex]) {
minIndex = j;
}
}
if (minIndex != i) {
(*moveCount) += 3; // 交换需要三步
swap(&array[i], &array[minIndex]);
}
}
}

// 快速排序
void quickSort(int array[], int low, int high, int* compCount, int* moveCount) {
if (low < high) {
int pivot = array[high];
int i = low - 1;
for (int j = low; j <= high - 1; j++) {
(*compCount)++;
if (array[j] < pivot) {
i++;
(*moveCount) += 3; // 交换需要三步
swap(&array[i], &array[j]);
}
}
(*moveCount) += 3; // 交换需要三步
swap(&array[i + 1], &array[high]);
int partitionIndex = i + 1;

quickSort(array, low, partitionIndex - 1, compCount, moveCount);
quickSort(array, partitionIndex + 1, high, compCount, moveCount);
}
}

// 希尔排序
void shellSort(int array[], int size, int* compCount, int* moveCount) {
for (int gap = size / 2; gap > 0; gap /= 2) {
for (int i = gap; i < size; i++) {
int temp = array[i];
int j;
for (j = i; j >= gap && array[j - gap] > temp; j -= gap) {
(*compCount)++;
array[j] = array[j - gap];
(*moveCount)++;
}
array[j] = temp;
(*moveCount)++;
}
}
}

// 堆植于索引 i 的子树
void heapify(int array[], int size, int i, int* compCount, int* moveCount) {
int largest = i; // 以 root 身份初始化 largest
int left = 2 * i + 1; // 左孩子
int right = 2 * i + 2; // 右孩子

// 如果左孩子大于根
if (left < size && array[left] > array[largest]) {
largest = left;
}
(*compCount)++;

// 如果右孩子大于目前最大的孩子largest
if (right < size && array[right] > array[largest]) {
largest = right;
}
(*compCount)++;

// 如果最大的不是根
if (largest != i) {
(*moveCount) += 3; // 交换需要三步
swap(&array[i], &array[largest]);

// 以递归方式堆受影响的子树
heapify(array, size, largest, compCount, moveCount);
}
}

// 堆排序
void heapSort(int array[], int size, int* compCount, int* moveCount) {
// 构建堆(重新排列数组)
for (int i = size / 2 - 1; i >= 0; i--) {
heapify(array, size, i, compCount, moveCount);
}

// 一个接一个地从堆中提取元素
for (int i = size - 1; i >= 0; i--) {
(*moveCount) += 3; // 交换需要三步
swap(&array[0], &array[i]);

// 在减少的堆上调用 最大堆
heapify(array, i, 0, compCount, moveCount);
}
}

int main() {
int array[SIZE];

for (int i = 0; i < 5; i++) {
generateRandomArray(array, SIZE);

int compCount = 0;
int moveCount = 0;

printf("输入数组 %d:\n", i + 1);

// 冒泡排序
int bubbleArray[SIZE];
for (int j = 0; j < SIZE; j++) {
bubbleArray[j] = array[j];
}
bubbleSort(bubbleArray, SIZE, &compCount, &moveCount);
printf("冒泡排序 - 比较: %d, 移动: %d\n", compCount, moveCount);

// 插入排序
int insertionArray[SIZE];
for (int j = 0; j < SIZE; j++) {
insertionArray[j] = array[j];
}
compCount = 0;
moveCount = 0;
insertionSort(insertionArray, SIZE, &compCount, &moveCount);
printf("插入排序 - 比较: %d, 移动: %d\n", compCount, moveCount);

// 选择排序
int selectionArray[SIZE];
for (int j = 0; j < SIZE; j++) {
selectionArray[j] = array[j];
}
compCount = 0;
moveCount = 0;
selectionSort(selectionArray, SIZE, &compCount, &moveCount);
printf("选择排序 - 比较: %d, 移动: %d\n", compCount, moveCount);

// 快速排序
int quickArray[SIZE];
for (int j = 0; j < SIZE; j++) {
quickArray[j] = array[j];
}
compCount = 0;
moveCount = 0;
quickSort(quickArray, 0, SIZE - 1, &compCount, &moveCount);
printf("快速排序 - 比较: %d, 移动: %d\n", compCount, moveCount);

// 希尔排序
int shellArray[SIZE];
for (int j = 0; j < SIZE; j++) {
shellArray[j] = array[j];
}
compCount = 0;
moveCount = 0;
shellSort(shellArray, SIZE, &compCount, &moveCount);
printf("希尔排序 - 比较: %d, 移动: %d\n", compCount, moveCount);

// 堆排序
int heapArray[SIZE];
for (int j = 0; j < SIZE; j++) {
heapArray[j] = array[j];
}
compCount = 0;
moveCount = 0;
heapSort(heapArray, SIZE, &compCount, &moveCount);
printf("堆排序 - 比较: %d, 移动: %d\n", compCount, moveCount);

printf("\n");
}

return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
输入数组 1:
冒泡排序 - 比较: 504510, 移动: 733728
插入排序 - 比较: 244576, 移动: 245580
选择排序 - 比较: 504510, 移动: 2991
快速排序 - 比较: 11518, 移动: 18087
希尔排序 - 比较: 7941, 移动: 15989
堆排序 - 比较: 19280, 移动: 27414

输入数组 2:
冒泡排序 - 比较: 504510, 移动: 733728
插入排序 - 比较: 244576, 移动: 245580
选择排序 - 比较: 504510, 移动: 2991
快速排序 - 比较: 11518, 移动: 18087
希尔排序 - 比较: 7941, 移动: 15989
堆排序 - 比较: 19280, 移动: 27414

输入数组 3:
冒泡排序 - 比较: 504510, 移动: 756063
插入排序 - 比较: 252021, 移动: 253025
选择排序 - 比较: 504510, 移动: 2997
快速排序 - 比较: 10914, 移动: 17664
希尔排序 - 比较: 7703, 移动: 15751
堆排序 - 比较: 19268, 移动: 27396

输入数组 4:
冒泡排序 - 比较: 504510, 移动: 756063
插入排序 - 比较: 252021, 移动: 253025
选择排序 - 比较: 504510, 移动: 2997
快速排序 - 比较: 10914, 移动: 17664
希尔排序 - 比较: 7703, 移动: 15751
堆排序 - 比较: 19268, 移动: 27396

输入数组 5:
冒泡排序 - 比较: 504510, 移动: 756063
插入排序 - 比较: 252021, 移动: 253025
选择排序 - 比较: 504510, 移动: 2997
快速排序 - 比较: 10914, 移动: 17664
希尔排序 - 比较: 7703, 移动: 15751
堆排序 - 比较: 19268, 移动: 27396