8.排序
8.1排序概述
排序分为内部排序和外部排序
8.2冒泡排序法
基本思想
对待排序记录关键字从后往前(逆序)进行多遍扫描,当发现相邻两个关键字的次序与排序要求的规则不符时,就将这两个记录进行交换。这样,关键字较小的记录将逐渐从后面向前面移动,就象气泡在水中向上浮一样,所以该算法也称为气泡排序法。
算法实现
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <conio.h>
4
5 void BubbleSort(int a[], int n) //冒泡排序
6 {
7 int i, j, t;
8 for (i = 0; i<n - 1; i++)
9 {
10 for (j = n - 1; j>i; j--)
11 {
12 if (a[j - 1]>a[j])
13 {
14 t = a[j - 1];
15 a[j - 1] = a[j];
16 a[j] = t;
17 }
18 }
19 printf("第%2d遍:", i + 1);
20 for (j = 0; j<n; j++)
21 printf("%d ", a[j]);
22 printf("
");
23 }
24 }
25 void BubbleSort1(int a[], int n) //改进冒泡排序
26 {
27 int i, j, t, flag = 0; //flag用来标记是否发生交换
28 for (i = 0; i<n - 1; i++)
29 {
30 for (j = n - 1; j>i; j--)
31 {
32 if (a[j - 1]>a[j])//交换数据
33 {
34 t = a[j - 1];
35 a[j - 1] = a[j];
36 a[j] = t;
37 flag = 1;
38 }
39 }
40 printf("第%2d遍:", i + 1);
41 for (j = 0; j<n; j++)
42 printf("%d ", a[j]);
43 printf("
");
44 if (flag == 0) //没发生交换,直接跳出循环
45 break;
46 else
47 flag = 0;
48 }
49 }
50 int main()
51 {
52 int i;
53 int a[6] = {69,65,90,37,92,6};
54 printf("原数据:");
55 for (i = 0; i<6; i++)
56 printf("%d ", a[i]);
57 printf("
");
58
59 BubbleSort(a, 6);
60 printf("冒泡排序后:
");
61 for (i = 0; i<6; i++)
62 printf("%d ", a[i]);
63 printf("
");
64
65 getch();
66 return 0;
67 }
附:
用产生的随机数排序
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <conio.h>
4 #include <time.h>
5 #define ARRAYLEN 6
6 int CreateData(int arr[], int n, int min, int max) //创建一个随机数组,a保存生成的数据,n为数组元素的数量
7 {
8 int i, j, flag;
9 srand(time(NULL));
10 if ((max - min + 1)<n)
11 return 0; //最大数与最小数之差小于产生数组的数量,生成数据不成功
12 for (i = 0; i<n; i++)
13 {
14 do
15 {
16 arr[i] = (max - min + 1)*rand() / (RAND_MAX + 1) + min; //生成数不能相同
17 flag = 0;
18 for (j = 0; j<i; j++)
19 {
20 if (arr[i] == arr[j]) //相同重新生成
21 flag = 1;
22 }
23 } while (flag);
24 }
25 return 1;
26 }
27
28 void BubbleSort(int a[], int n) //冒泡排序
29 {
30 int i, j, t;
31 for (i = 0; i<n - 1; i++)
32 {
33 for (j = n - 1; j>i; j--)
34 {
35 if (a[j - 1]>a[j])
36 {
37 t = a[j - 1];
38 a[j - 1] = a[j];
39 a[j] = t;
40 }
41 }
42 printf("第%2d遍:", i + 1);
43 for (j = 0; j<n; j++)
44 printf("%d ", a[j]);
45 printf("
");
46 }
47 }
48 void BubbleSort1(int a[], int n) //改进冒泡排序
49 {
50 int i, j, t, flag = 0; //flag用来标记是否发生交换
51 for (i = 0; i<n - 1; i++)
52 {
53 for (j = n - 1; j>i; j--)
54 {
55 if (a[j - 1]>a[j])//交换数据
56 {
57 t = a[j - 1];
58 a[j - 1] = a[j];
59 a[j] = t;
60 flag = 1;
61 }
62 }
63 printf("第%2d遍:", i + 1);
64 for (j = 0; j<n; j++)
65 printf("%d ", a[j]);
66 printf("
");
67 if (flag == 0) //没发生交换,直接跳出循环
68 break;
69 else
70 flag = 0;
71 }
72 }
73 int main()
74 {
75 int i, a[ARRAYLEN];
76 for (i = 0; i<ARRAYLEN; i++)
77 a[i] = 0;
78
79 if (!CreateData(a, ARRAYLEN, 1, 100))
80 {
81 printf("生成随机数不成功!
");
82 getch();
83 return 1;
84 }
85
86 printf("原数据:");
87 for (i = 0; i<ARRAYLEN; i++)
88 printf("%d ", a[i]);
89 printf("
");
90
91 BubbleSort1(a, ARRAYLEN);
92 printf("排序后:");
93 for (i = 0; i<ARRAYLEN; i++)
94 printf("%d ", a[i]);
95 printf("
");
96
97 getch();
98 return 0;
99 }
8.3快速排序法
基本思想:
快速排序使用分治策略来把待排序数据序列分为两个子序列,具体步骤为:
(1)从数列中挑出一个元素,称该元素为“基准”。
(2)扫描一遍数列,将所有比“基准”小的元素排在基准前面,所有比“基准”大的元素排在基准后面。
(3)通过递归,将各子序列划分为更小的序列,直到把小于基准值元素的子数列和大于基准值元素的子数列排序。
算法实现:
1 #include <stdio.h>
2 #include <conio.h>
3 #define ARRAYLEN 8
4 int Division(int a[], int left, int right) //分割
5 {
6 int base = a[left]; //基准元素
7 while (left<right)
8 {
9 while (left<right && a[right]>base)
10 --right; //从右向左找第一个比基准小的元素
11 a[left] = a[right];
12 while (left<right && a[left]<base)
13 ++left; //从左向右找第一个比基准大的元素
14 a[right] = a[left];
15 }
16 a[left] = base;
17 return left;
18 }
19 void QuickSort(int a[], int left, int right)
20 {
21 int i, j;
22 if (left<right)
23 {
24 i = Division(a, left, right); //分割
25 QuickSort(a, left, i - 1); //将两部分分别排序
26 QuickSort(a, i + 1, right);
27 }
28 }
29 int main()
30 {
31 int i, a[ARRAYLEN] = {69,65,90,37,92,6,28,54};
32 printf("原数据:");
33 for (i = 0; i<ARRAYLEN; i++)
34 printf("%d ", a[i]);
35 printf("
");
36
37 QuickSort(a, 0, ARRAYLEN - 1);
38 printf("排序后:");
39 for (i = 0; i<ARRAYLEN; i++)
40 printf("%d ", a[i]);
41 printf("
");
42
43 getch();
44 return 0;
45 }
8.4选择排序法
思想:
选择排序(Selection Sort)的基本思想:对n个记录进行扫描,选择最小的记录,将其输出,接着在剩下的n-1个记录中扫描,选择最小的记录将其输出,……不断重复这个过程,直到只剩一个记录为止。
算法实现:
1 #include <stdio.h>
2 #include <conio.h>
3 #define ARRAYLEN 8
4 void SelectSort(int a[], int n)
5 {
6 int i, j, t, k;
7 for (i = 0; i<n - 1; i++)
8 {
9 k = i;
10 for (j = i; j<n; j++){
11 if (a[k]>a[j])
12 k = j;
13 }
14 t = a[i];
15 a[i] = a[k];
16 a[k] = t;
17 }
18 }
19 int main()
20 {
21 int i, a[ARRAYLEN] = {69,65,90,37,92,6,28,54};
22 printf("原数据:");
23 for (i = 0; i<ARRAYLEN; i++)
24 printf("%d ", a[i]);
25 printf("
");
26
27 SelectSort(a,ARRAYLEN);
28 printf("排序后:");
29 for (i = 0; i<ARRAYLEN; i++)
30 printf("%d ", a[i]);
31 printf("
");
32
33 getch();
34 return 0;
35 }
8.5堆排序法
基本思想:
堆是一个完全二叉树,树中每个结点对应于原始数据的一个记录,并且每个结点应满足以下条件:非叶结点的数据大于或等于其左、右孩子结点的数据(若是按从大到小的顺序排序,则要求非叶结点的数据小于或等于其左、右孩子结点的数据)。
由堆的定义可看出,其根结点为最大值,堆排序就是利用这一特点进行的。堆排序过程包括两个阶段:
(1)将无序的数据构成堆(即用无序数据生成满足堆定义的完全二叉树)。
(2)利用堆排序(即用上一步生成的堆输出有序的数据)。
例:对69、65、90、37、92、6、28、54堆排序
算法实现:
1 #include <stdio.h>
2 #include <conio.h>
3 #define ARRAYLEN 8
4 void HeapAdjust(int a[], int s, int n)//构成堆
5 {
6 int j, t;
7 while (2 * s <n) //第s个结点有右子树
8 {
9 j = 2 * s ;
10 if ((j + 1)<n)
11 {
12 if (a[j]<a[j + 1])//右左子树小于右子树,则需要比较右子树
13 j++; //序号增加1,指向右子树
14 }
15 if (a[s]<a[j])//比较s与j为序号的数据
16 {
17 t = a[s]; //交换数据
18 a[s] = a[j];
19 a[j] = t;
20 s = j;//堆被破坏,需要重新调整
21 }
22 else //比较左右孩子均大则堆未破坏,不再需要调整
23 break;
24 }
25 }
26 void HeapSort(int a[], int n)//堆排序
27 {
28 int t, i;
29 int j;
30 for (i = n/2-1 ; i >= 0; i--) //将a[0,n-1]建成大根堆
31 HeapAdjust(a, i, n);
32 for (i = n - 1; i>0; i--)
33 {
34 t = a[0];//与第i个记录交换
35 a[0] = a[i];
36 a[i] = t;
37 HeapAdjust(a, 0, i); //将a[0]至a[i]重新调整为堆
38 }
39 }
40 int main()
41 {
42 int i, a[ARRAYLEN] = {69,65,90,37,92,6,28,54};
43 printf("原数据:");
44 for (i = 0; i<ARRAYLEN; i++)
45 printf("%d ", a[i]);
46 printf("
");
47
48 HeapSort(a,ARRAYLEN);
49 printf("排序后:");
50 for (i = 0; i<ARRAYLEN; i++)
51 printf("%d ", a[i]);
52 printf("
");
53
54 getch();
55 return 0;
56 }
8.6插入排序法
基本思想:
插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后移动,为最新元素提供插入空间。
算法实现:
1 #include <stdio.h>
2 #include <conio.h>
3 #define ARRAYLEN 8
4 void InsertSort(int a[], int n)//直接插入排序
5 {
6 int i, j, t;
7 for (i = 1; i<n; i++)
8 {
9 t = a[i]; //取出一个未排序的数据
10 for (j = i - 1; j >= 0 && t<a[j]; --j) //在排序序列中查找位置
11 a[j + 1] = a[j]; //向后移动数据
12 a[j + 1] = t; //插入数据到序列
13 }
14 }
15 int main()
16 {
17 int i, a[ARRAYLEN] = {69,65,90,37,92,6,28,54};
18 printf("原数据:");
19 for (i = 0; i<ARRAYLEN; i++)
20 printf("%d ", a[i]);
21 printf("
");
22
23 InsertSort(a,ARRAYLEN);
24 printf("排序后:");
25 for (i = 0; i<ARRAYLEN; i++)
26 printf("%d ", a[i]);
27 printf("
");
28
29 getch();
30 return 0;
31 }
8.7希尔(shell)排序法
基本思想:
希尔排序又称为缩小增量排序,也属于插入排序类的算法,是对直接插入排序的一种改进。
基本思想就是:将需要排序的序列划分为若干个较小的序列,对这些序列进行直接插入排序,通过这样的操作可使用需要排序的数列基本有序,最后再使用一次直接插入排序。这样,首先对数量较小的序列进行直接插入排序可提高效率,最后对基本有序的序列进行直拦插入排序,也可提高效率,从而使整个排序过程的效率得到提升。
算法实现:
1 #include <stdio.h>
2 #include <conio.h>
3 #define ARRAYLEN 8
4 void ShellSort(int a[], int n)//希尔排序
5 {
6 int d, i, j, x;
7 d = n / 2;
8 while (d >= 1) //循环至增量为1时结束
9 {
10 for (i = d; i<n; i++)
11 {
12 x = a[i]; //获取序列中的下一个数据
13 j = i - d; //序列中前一个数据的序号
14 while (j >= 0 && a[j]>x) //下一个数大于前一个数
15 {
16 a[j + d] = a[j]; //将后一个数向前移动
17 j = j - d; //修改序号,继续向前比较
18 }
19 a[j + d] = x; //保存数据
20 }
21 d /= 2; //缩小增量
22 }
23 }
24 int main()
25 {
26 int i, a[ARRAYLEN] = {69,65,90,37,92,6,28,54};
27 printf("原数据:");
28 for (i = 0; i<ARRAYLEN; i++)
29 printf("%d ", a[i]);
30 printf("
");
31
32 ShellSort(a,ARRAYLEN);
33 printf("排序后:");
34 for (i = 0; i<ARRAYLEN; i++)
35 printf("%d ", a[i]);
36 printf("
");
37
38 getch();
39 return 0;
40 }
8.8合并(归并)排序法
基本思想:
合并排序(Merge Sort)就是将两个或多个有序表合并成一个有序表。
算法实现:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <conio.h>
4 #define ARRAYLEN 8
5 void MergeStep(int a[], int r[], int s, int m, int n) //相邻有序段合并
6 {
7 int i, j, k;
8 k = s;
9 i = s;
10 j = m + 1;
11 while (i <= m && j <= n) //当两个有序表都未结束时,循环比较
12 {
13 if (a[i] <= a[j]) //当较小的元素复制到R中
14 r[k++] = a[i++];
15 else
16 r[k++] = a[j++];
17 }
18 while (i <= m) //将未合并的部分复制到R中
19 r[k++] = a[i++];
20 while (j <= n)
21 r[k++] = a[j++]; //将未合并的部分复制到R中
22 }
23 void MergePass(int a[], int r[], int n, int len) //完成一遍合并的函数
24 {
25 int s, e;
26 s = 0;
27 while (s + len<n) //至少有两个有序段
28 {
29 e = s + 2 * len - 1;
30 if (e >= n) //最后一段可能少于len个结点
31 e = n - 1;
32 MergeStep(a, r, s, s + len - 1, e); //相邻有序段合并
33 s = e + 1; //下一对有序段中左段的开始下标
34 }
35 if (s<n) //还剩一个有序段,将其从A中复制到R中
36 for (; s<n; s++)
37 r[s] = a[s];
38 }
39 void MergeSort(int a[], int n)
40 {
41 int *p;
42 int len = 1; //有序序列的长度
43 int f = 0; //变量f作标志
44 if (!(p = (int *)malloc(sizeof(int)*n))) //分配内存空间,保存临时数据
45 {
46 printf("分配临时内存失败!
");
47 exit(0);
48 }
49 while (len<n)
50 {
51 if (f) //交替地在A和P之间来回合并
52 MergePass(p, a, n, len); //调用MergePass,对p合并到a
53 else
54 MergePass(a, p, n, len); //调用MergePass,对a合并到p
55 len *= 2; //增加有序序列长度
56 f = 1 - f; //使f值在0和1之间切换
57 }
58 if (f) //若进行了排序
59 for (f = 0; f<n; f++) //将数组p中的数据复制到数组a
60 a[f] = p[f];
61 free(p); //释放分配的内存
62 }
63
64 int main()
65 {
66 int i, a[ARRAYLEN] = {69,65,90,37,92,6,28,54};
67 printf("原数据:");
68 for (i = 0; i<ARRAYLEN; i++)
69 printf("%d ", a[i]);
70 printf("
");
71
72 MergeSort(a,ARRAYLEN);
73 printf("排序后:");
74 for (i = 0; i<ARRAYLEN; i++)
75 printf("%d ", a[i]);
76 printf("
");
77
78 getch();
79 return 0;
80 }