排序算法
本文最后更新于 2024年9月18日 凌晨
Warning
此处不对排序的空间和时间复杂度进行分析,关注排序原理和代码实现。
排序算法
冒泡排序、选择排序、插入排序
这几种排序是较为基础且容易理解的所以就直接放代码了。
冒泡排序
代码如下: 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#include<stdio.h>
#include<stdlib.h>
#include <time.h>
int a[1000001];
void random(int n);//随机数
void bubble_sort(int q);
int main()
{
printf("参与排序的随机数个数:");
int n;
scanf("%d", &n);
random(n);
double start, finish;
start = (double)clock();
bubble_sort(n);
for (int i = 1; i <= n; i++)printf("%d ", a[i]);
finish = (double)clock();
printf("%lf ms", finish - start);//运行时间
return 0;
}
void bubble_sort(int q)
{
for (int i = 1; i < q; i++) {
for (int j = q - 1; j >= i; j--) {
if (a[j + 1] < a[j]) {
int tmp = a[j + 1]; a[j + 1] = a[j]; a[j] = tmp;
}
}
}
}
void random(int n)
{
for (int i = 1; i <= n; i++) {
srand(i);
a[i] = rand() % 100;
}
}
选择排序
代码如下: 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#include<stdio.h>
#include<stdlib.h>
#include <time.h>
int a[1000001];
void random(int n);//随机数
void selection_sort(int q);
int main()
{
printf("参与排序的随机数个数:");
int n;
scanf("%d", &n);
random(n);
double start, finish;
start = (double)clock();
selection_sort(n);
for (int i = 1; i <= n; i++)printf("%d ", a[i]);
finish = (double)clock();
printf("%lf ms", finish - start);//运行时间
return 0;
}
void selection_sort(int q)
{
for (int i = 1; i < q; i++) {
int k = i;
for (int j = i + 1; j <= q; j++) {
if (a[k] > a[j]) {
k = j;
}
}
if (k != i) {
int tmp = a[k]; a[k] = a[i]; a[i] = tmp;
}
}
}
void random(int n)
{
for (int i = 1; i <= n; i++) {
srand(i);
a[i] = rand() % 100;
}
}
插入排序
代码如下: 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#include<stdio.h>
#include<stdlib.h>
#include <time.h>
int a[1000001];
void random(int n);//随机数
void insertion_sort(int q);
int main()
{
printf("参与排序的随机数个数:");
int n;
scanf("%d", &n);
double start, finish;
start = (double)clock();
random(n);
insertion_sort(n);
for (int i = 1; i <= n; i++)printf("%d ", a[i]);
finish = (double)clock();
printf("%lf ms", finish - start);//运行时间
return 0;
}
void insertion_sort(int q)
{
for (int i = 2; i <= q; i++) {
for (int j = i - 1; j >= 1; j--) {
if (a[j] <= a[i]) {
int tmp = a[i];//插入
for (int k = i - 1; k > j; k--) {
a[k + 1] = a[k];
}
a[j + 1] = tmp;
}
}
}
}
void random(int n)
{
for (int i = 1; i <= n; i++) {
srand(i);
a[i] = rand() % 100;
}
}
桶排序
原理:对于确定范围的若干个数排序,先放上包括所有数据范围的桶,把数据放到对应编号的桶中,输出按照桶的编号从小到大输出。
假设数据范围为0~8:
将数据全部放入桶中:
结果:
代码如下: 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#include<stdio.h>
#include<stdlib.h>
#include <time.h>
void random(int n);//随机数
int a[10000004], b[10000004];
int main()
{
printf("参与排序的随机数个数:");
int n;
scanf("%d", &n);
double start, finish;
start = (double)clock();
random(n);
for (int i = 1; i <= n; i++)b[a[i]]++;
for (int i = 1; i <= 10000003; i++) {
while (b[i] != 0) {
printf("%d ", i);
b[i]--;
}
}
finish = (double)clock();
printf("%lf ms", finish - start);//运行时间
return 0;
}
void random(int n)
{
for (int i = 1; i <= n; i++) {
a[i] = rand() % 10000000;
}
}
原理:对于组数,按照位数从右向左对每个数进行排序。由于单个数字只有0~9故使用桶排序来提高效率。
假设有如下数据:
第一次操作:
第二次操作:
\[ \dots \] 最后的结果:
代码如下:
1 |
|
由于内存占用大,关键字值域小时可以采用
快速排序
单次操作:选择一个参照的数据(一般是第一个数),下标 i , j 分别从数组的头和尾开始,i , j 相向遍历,当下标 i 所指的数大于参照数据,下标 j 所指的数据小于参照数据时,两个数据相互换位置。 并且当i=j时,参照数据位置和 i/j 指向的数据交换位置,结束本次操作。
单次结果:参照数据被排在正确的位置,将原数组分为左右两个区间。
整体:通过递归使各个数据归位,完成排序。
这里提供作者 大卫·加勒斯 的quick sort动态图地址。
代码如下:
1 |
|
同时c语言中也提供qsort()函数,在<stdlib.h>中,使用方法如下:
1 |
|
归并排序
合并数组:取两组中 最小/最大 的数进行比较,并依照大小先后放入结果数组。
实现:对于一个无序数组a[N] \[ a[1],a[2],a[3] \dots a[N] \] 第一次:先处理 a[1],a[2] 形成一个有序数组 a[1-2] ,再处理 a[3],a[4] 形成一个有序数组 a[3-4] ,将 a[1-2] 和 a[3-4] 合并为 a[1-4] \[ a[1-2],a[3-4],a[5] \dots a[N] \] \[ a[1-4],a[5],a[6] \dots a[N] \] 第二次:同样的方法将 a[5] , a[6] , a[7] , a[8] 处理为有序数组 a[5-8] ,将 a[1-4] 和 a[5-8] 合并为 a[1-8] \[ a[1-4],a[5-8],a[9] \dots a[N] \] \[ a[1-8],a[9],a[10] \dots a[N] \] 第三次:同样的方法将 a[9] , a[10] , a[11] , a[12] , a[13] , a[14] , a[15] , a[16] 处理为有序数组 a[9-16] ,将 a[1-8] 和 a[9-16]合并为 a[1-16] \[ a[1-8],a[9-16],a[17] \dots a[N] \] \[ a[1-16],a[17],a[18] \dots a[N] \] \[ \dots \] 最后结果得到一个有序的数组。
这里提供作者 大卫·加勒斯 的merge sort动态图地址
(合并数组)代码如下:
1 |
|
归并排序完整代码如下:
1 |
|
堆排序
这种排序可以看作是一种选择排序的优化.。
实现:首先要形成树,这里从最低的根部逐个向上对树进行生成。第一次维护后的树只能保证树的根为最大的数,因此将最大的数转移到树维护范围的最后一个数据(最后一个数的正确位置),并且将此时的最后一个数据移出树的维护范围,一直到将所有数都放到正确的位置,达到排序的效果。
假设数据如图:
第一次操作(生成树):
第二次操作:
\[ \dots \]
最后结果:
代码如下:
1 |
|
希尔排序
这种排序可以看作是插入排序的优化。
那为什么说希尔排序是针对插入排序的一种优化呢? 我们都知道,插入排序在两种情况下工作量最小
在大多数元素基本有序的情况下。
元素基本有序的话,元素之间也就不需要频繁地交换,工作量自然减少在元素数量比较少的情况下。
元素数量n变小,时间复杂度O(n^2)自然也变小。
作者观点:希尔排序相较于传统的插入排序在一定程度上减少了数据交换次数,提高了效率。
下面是原理图解。
第一次操作:
第二次操作:
第三次操作:
最终结果:
代码如下:
1 |
|