C语言进阶之数组② --- 连续排序、冒泡排序、插入排序、二分查找

目录

一、选择排序

二、冒泡排序

 三、插入排序

四、查找算法


前言:在学习排序的过程中,需要注意掌握每一个排序的三个要点:了解算法思想 ,掌握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 }

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值