目录
前言:在学习排序的过程中,需要注意掌握每一个排序的三个要点:了解算法思想 ,掌握C语言如何实现的,如何应用.
补充:1.从键盘输入一串数组元素,如何找出这个数组中的最大值,下面是示范代码。
其主要思想是:先把一个值a[0]拿出来,充当最大值max,然后依次从a[1]开始往后的值,逐个与max比较,若a[i]比max大,则把a[i]的值赋给max。遍历完数组后,max里的值就是最大值,整个过程类似于其它数组元素与 a[0] “打擂台”。
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int n =0;
6 scanf("%d", &n);
7 int a[n];
8 int i, max;
9
10 for (i=0; i<n; ++i )
11 {
12 scanf("%d", &a[i]);
13 }
14
15 max = a[0];
16 for(i=0; i<n; ++i)
17 {
18 if (a[i] > max)
19 {
20 max = a[i];
21
22 }
23 }
24
25 printf("max = %d\n", max);
26 }
2.将一个数组里的元素逆序(reverse)输出,代码实例如下
主要思路是:有一个数组a[] = {1,2,3,4,5},要实现逆序输出,即a[0]需要与a[4]换位,a[1]需要与a[3]换位......这个过程需要多次进行操作,因此需要把它放到循环中实现,即a[i]需要与a[len-i-1]环卫,需要注意的是循环的结束条件是i < len/2,否则换好的值会再次被更换。最后用循环对更新后的数组输出,这样就可以实现数组的逆序输出。
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int a[] = {1, 2, 3, 4, 5};
6 int i, temp;
7 int len = sizeof(a) / sizeof(a[0]); //计算数组长度
8
9 for (i=0; i<len/2; ++i)
10
11 {
12 temp = a[i];
13 a[i] = a[len-i-1];
14 a[len-i-1] = temp;
15 } // for循环中实现数的交换
16
17 for (i=0; i<len; i++)
18 {
19 printf(" %d", a[i]);
20 }
21
22 printf("\n");
23
24 return 0;
25 }
一、选择排序
思想:给合适的位置选择合适的数
过程:以下过程用len表示数组长度,下标编号最大值是len-1,升序排列
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
1. 确定0号位置的值,即找最小值,和找出数组中的最大值类似。先选定a[0]为最小值,这样从a[1]开始,后面的数组元素都要依次于 a[0] 比较。如果有数值比原先的a[0] 小,则交换它们的位置。通俗一点理解,就相当于后面的元素在和 a[0] “打擂台”,谁小,谁就进去0号位置。
2. 确定1号位置的值,此时0 号位置的值已经确定,不用再管。现在1号位置作为新 “擂台”,a[1] 与从a[2] 开始,后面的数组元素依次比较,再选出一个最小的值放入1号位置。
。。。。。。
后面以此类推,最后确定 len-2 位置上的值时,能与该位置上的值比较的值,只剩 len-1 位置的值。当a[ len-2 ] > a[ len-1 ] 时,两者交换位置,比较结束 ;当a[ len-2 ] < a[ len-1 ] 时,两者位置不变,比较也结束。因此 len-2 是最后一个需要确定位置的值,代入上图就是a[6]的位置。
每个位置的值的确定都类似于找最大值的过程,用循环实现,最外层的需要确定几号位置,也需要一个循环。其大体框架如下
① i < len -1
因为len -2 是最后一个需要确定的位置,因此 i 最后取到的值就是len-2
②j = i + 1
此时 i 是“打擂台”的位置,比较都是从“擂台” 的下一位开始的,因此用 j = i + 1作为循环开始的条件
以下是代码实例:
1 # include <stdio.h>
2
3 int main(void)
4 {
5 int i, j, temp;
6 int a[] = {23,1,5,21,18,99};
7 int len = sizeof(a) / sizeof(a[0]);
8
9 for (i=0; i < len-1; ++i)
10
11 {
12 for (j=i+1; j<len; ++j)
13
14 {
15 if(a[i] > a[j])
16 {
17 temp = a[i];
18 a[i] = a[j];
19 a[j] = temp;
20 }
21 }
22 }
23
24 for (i=0; i<len; ++i)
25 {
26 printf(" %d", a[i]);
27 }
28 printf("\n");
29
30 return 0;
31 }
二、冒泡排序
思想:一次冒出一个数,相邻两个元素两两比较,小的放前,大的放后
过程:同样地以下过程用len表示数组长度,下标编号最大值是len-1,升序排列
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
1. 第一趟,需要比较8个数,由于是两两比较,所以比较的次数是7次。先把a[0] 与 a[1] 作比较,小的放前,大的放后,a[1] 再与 a[2] 作比较,也是小的放前,大的放后。以此类推比较后面的值,比完第一趟后,a[ len -1 ] 中的值就是最大值,就固定下来不用管了,对应上图就是 a[7] 的位置。
2. 第二趟,需要比较前7个数,比较的次数是6次,同样地,把相邻的两组数进行比较,小的放前,大的放后。这样,比完第二趟后,a [len -2 ] 的值也固定下来了,对应图上的a[6]的位置
。。。。。。
第n-1 趟后,只需比较前两个数,比较次数是1次。
在上述过程中,需要注意趟数与次数之间对应的数学关系,用一个循环表示趟数,一个表示里面的交换过程。大致框架如下:其中 n = len,是数组长度
①for(i = 1;i<n; ++i)
外层循环控制趟数,长度为n的数组,一定需要比较n - 1 趟,才可以把所有的数排列完
② for(j=0;j<n-i; ++j)
第1趟时,需要比较7次
第2趟时,需要比较6次
......
第n-1趟时,只需比较1次。
除此之外还有别的写法:
本质上这三种写法都是一样的,都是需要保证趟数是 n-1 趟,理清趟数与比较次数的对应关系。
以下是代码实例:
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int i, j, temp;
6 int a[] = {12,5,43,87,43,9};
7 int len = sizeof(a) / sizeof(a[0]);
8
9 for(i=1; i<len; ++i)
10 {
11 for (j=0; j<len-i; ++j)
12 if(a[j] > a[j+1])
13 {
14 temp = a[j];
15 a[j] = a[j+1];
16 a[j+1] = temp;
17 }
18 }
19
20 for (i=0; i<len; ++i)
21 printf(" %d", a[i]);
22
23 printf("\n");
24
25 return 0;
26 }
三、插入排序
思想:在有序序列中,找到合适的位置插入 (两个数组,非原地插入)
a数组 | a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
b数组 | b[0] | b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] |
过程:先从a数组中拿出a[0],放到b[0]中,此时只有一位数,不用比较。
接着拿出a[1],与b数组中的a[0]作比较,若a[0] 比a[1] 大,则a[0] 挪到 b的1号位置,把a[1] 放到b的0号位置中;
再拿出a[2],与b的1号位置的值比较,若b[1] 比 a[2]大,则b[1]挪到b的2号位置,b的1号位置空了出来,再接着与b的0号位置比较,若b[1] 比 a[2]大,则b[0]挪到b的1号位置,b的0号位置空了出来,前面没有数与a[2] 比了,就把a[2]放到b的0号位置。
。。。。。。
以此类推,直至从a数组中拿出最后一个数,并与b数组中的值比较完。注意,整个过程中,b数组的值都是有序的,只是每次都从a数组里面拿数出来,在b中找合适的位置插入,直至拿完a数组的数为止。以下是大致框架:
①while(j > 0 && t < b[j-1])
整个while语句都是找位置的过程。跳出循环的条件1是j >0,即当j = 0时,表明已经全部比完,j已经比到了b[0]的位置,此时拿到的数放在b[0]中;
跳出循环的条件2是 t < b[j-1] ,即拿出的值比j的前一个值大,不用比了,直接放入下一个空位。
补充:考虑到空间复杂度的因素,还有原地插入,比非原地插入更节省空间,即只在a数组中做插入排序,思想与非原地插入类似,以下是大致框架:
下面是代码实例:非原地插入
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int i, j, t;
6 int a[] = {34,12,6,77,23,56};
7 int len = sizeof(a) / sizeof(a[0]);
8 int b[len];
9
10 for(i=0; i<len; ++i)
11 {
12 t = a[i];
13 j = i;
14 while(t<b[j-1] && j>0)
15 {
16 b[j] = b[j-1];
17 --j;
18 }
19
20 b[j] = t;
21 }
22
23 for(i=0; i<len; ++i)
24 printf(" %d", b[i]);
25 printf("\n");
26
27 return 0;
28 }
~
原地插入:比较常用
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int i, j, t;
6 int a[] = {34,12,6,77,23,56};
7 int len = sizeof(a) / sizeof(a[0]);
8
9 for (i=1; i<len; ++i)
10 {
11 t = a[i];
12 j = i;
13 while (t<a[j-1] && j>0)
14 {
15 a[j] = a[j-1];
16 --j;
17 }
18
19 a[j] = t;
20 }
21
22 for(i=0; i<len; ++i)
23 printf(" %d", a[i]);
24 printf("\n");
25
26 return 0;
27 }
~
四、查找算法
主要介绍二分查找(折半查找) ,大前提是数据得是有序的
思想:
1. 找到中间位置的数, //下标
2. 拿这个数 和 要找的数比较
让begin代表数组中最小的下标,end代表数组中最大的下标。
若 a[mid] > m --- 代表m落在数组下半部分,令end = mid -1,缩小范围,再继续找中间的一半进行比较。
若a[mid] < m --- 代表m落在数组上半部分,令begin = mid +1,缩小范围,再继续找中间的一半进行比较。
以下是大致框架:
关于if语句:while结束的情况有两种:①找到了,break直接跳出循环,此时begin <= end
②没找到,执行完while语句出来的,此时begin > end
以下是代码实例:(需要自己补充排序代码)
26 int mid, begin, end, m;
27 printf("Input a number:");
28 scanf("%d", &m);
29 begin = 0;
30 end = len-1;
31
32 mid = (begin + end) / 2;
33
34 while(begin <= end)
35 {
36 if(a[mid] > m)
37 {
38 end = mid - 1;
39 }
40 else if(a[mid] < m)
41 {
42 begin = mid + 1;
43 }
44 else
45 break;
46 }
47
48 if(begin <= end)
49 {
50 printf("found!");
51 }
52 else
53 printf("no found!");
54
55 return 0;
56 }