三种基本排序(以升序为例)

三种基本排序(以升序为例)

1.冒泡排序

思想:每次相邻两个数比较,若升序,则将大的数放到后面,一次循环过后,就会将最大的数放在最后.

如图9 3 2 5 8 4 7 6是输入的待排序的数列,经过第一次排序,将最大的9放在最后,第二次排序,将剩下的2 3 5 4 7 6 8进行冒泡,将当前最大的8放在倒数第二的位置,以此类推

接下来上代码


 
 
  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int a[ 1001];
  5. int n,i,j,t;
  6. scanf( "%d",&n); //n为要排序的数的个数
  7. //输入要排序的数
  8. for(i= 0;i<n;++i)
  9. scanf( "%d",a+i);
  10. //接下来进行排序
  11. for(i= 0;i<n -1;++i) //n个数,总共需要进行n-1次
  12. { //n-1个数排完,第一个数一定已经归位
  13. //每次会将最大(升序)或最小(降序)放到最后面
  14. for(j= 0;j<n-i -1;++j)
  15. {
  16. if(a[j]>a[j+ 1]) //每次冒泡,进行交换
  17. {
  18. t=a[j];
  19. a[j]=a[j+ 1];
  20. a[j+ 1]=t;
  21. }
  22. }
  23. for(j= 0;j<n;++j)
  24. printf( "%-5d ",a[j]);
  25. printf( "\n\n");
  26. }
  27. return 0;
  28. }

可见冒泡的时间复杂度是O(N²),可以对冒泡排序进行优化,若排序的过程中,序列已变为有序,则不会进行冒泡,所以之后的排序就不用进行了,这样的话,序列越是有序,则时间复杂度会趋于O(N),以下为改进后的代码


 
 
  1. for(i= 0;i<n- 1;++i) //n个数,总共需要进行n-1次
  2. { //n-1个数排完,第一个数一定已经归位
  3. //每次会将最大(升序)或最小(降序)放到最后面
  4. int f= 1; //这里设置一个开关变量
  5. for(j= 0;j<n-i- 1;++j)
  6. {
  7. if(a[j]>a[j+ 1])
  8. {
  9. t=a[j];
  10. a[j]=a[j+ 1];
  11. a[j+ 1]=t;
  12. f= 0;
  13. }
  14. }
  15. if( 1==f) //f为1说明没进行过冒泡,说明序列有序
  16. break; //若序列有序,则跳出排序即可
  17. }

2.选择排序

思想:从第一个数开始,每次和后面剩余的数进行比较,若升序,则如果后边的数比当前数字小,进行交换,和后面的所有的数比较、交换后,就会将当前的最小值放在当前的位置

输入的序列为9 3 2 5 8 4 7 6进行一次排序后将最小的数放在了第一位(a[0]与它后面的所有数进行比较,若a[0]比后面的数大,进行交换)后面的也是这个道理

接下来上代码


 
 
  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int a[ 1001];
  5. int n,i,j,t;
  6. scanf( "%d",&n); //n为要排序的数的个数
  7. //输入需要排序的数
  8. for(i= 0;i<n;++i)
  9. scanf( "%d",a+i);
  10. //接下来进行排序
  11. for(i= 0;i<n -1;++i) //因为每次需要和a[i]后面的数进行比较,所以到a[n-2](倒数第2个元素)就行
  12. {
  13. for(j=i+ 1;j<n;++j) //j从i后一个开始,a[i]与a[j]进行比较
  14. {
  15. if(a[i]>a[j]) //a[i]为当前值,若是比后面的a[j]大,进行交换
  16. {
  17. t=a[i];
  18. a[i]=a[j];
  19. a[j]=t;
  20. }
  21. } //每排序一次,就会将a[i](包括a[i])之后的最小值放在a[i]的位置
  22. for(j= 0;j<n;++j)
  23. printf( "%-5d",a[j]);
  24. printf( "\n\n");
  25. }
  26. return 0;
  27. }

可见,线则排序的时间复杂度也为O(N²),我们无法降低它的时间复杂度,但是也可进行优化,每次选择排序,都需要进行好多次的交换,这样也比较浪费资源,我们只需要用一个变量来存储最小值的下标(升序排序),最后进行交换,也可以达到目的,这样会节省很多时间,改进后代码如下


 
 
  1. for(i= 0;i<n- 1;++i)
  2. {
  3. int k=i; //设置一个变量存储下标
  4. for(j=i+ 1;j<n;++j) //遍历a[i]后的数
  5. {
  6. if(a[k]>a[j])
  7. k=j;
  8. } //执行过后,k会存储当前最小值的下标
  9. //进行一次交换
  10. t=a[k];
  11. a[k]=a[i];
  12. a[i]=t;
  13. }

3.插入排序

思想:我们都玩过扑克牌,我们也会习惯性的把牌按一定的顺序排序,这和插入排序的思想极为相似

我们用扑克的方法解释,首先我们抽到第一张牌,将它放在第一位,我们排序是从第二次抽牌开始,第二次抽起一张牌3,它比9小,所以将9向后移一位然后把3放在9原来的位置.再次抽牌2,发现它应该再3的前面,所以将3和9向后移,把2放到3原来的位置... ... 如图,蓝色标明了顺序.

接下来上代码


 
 
  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int a[ 1001];
  5. int i,j,t,n;
  6. scanf( "%d",&n); //输入要排序的数的个数
  7. for(i= 0;i<n;++i) //输入要排序的数
  8. scanf( "%d",a+i);
  9. for(i= 1;i<n;++i)
  10. {
  11. t=a[i]; //将a[i]“拿在手中”
  12. for(j=i -1;j> -1 && a[j]>t;j--) //和前面的牌比
  13. {
  14. //如果前面的"牌"比手中的"牌"大,则将"牌"向后移
  15. a[j+ 1]=a[j];
  16. } //跳出循环则表明了"手中的牌的位置找到了"
  17. a[j+ 1]=t; //将"牌"插入
  18. for(j= 0;j<n;++j)
  19. printf( "%-5d",a[j]);
  20. printf( "\n\n");
  21. }
  22. return 0;
  23. }

时间复杂度O(N²),若数列基本有序,则时间复杂度会趋于O(N)

谢谢大家很耐心的看完这篇博客,希望对加大有所帮助.本人纯手打,转载请注明出处,谢谢.

 


后续更新

插入排序的递归实现:


 
 
  1. void Insert_sort( int p[], int left, int right){
  2. if( left>= right)
  3. return ;
  4. Insert_sort(p, left, right -1);
  5. int temp;
  6. int i;
  7. temp=p[ right];
  8. for(i= right;i> left && p[i -1]>temp;--i)
  9. p[i]=p[i -1];
  10. p[i]=temp;
  11. for( int j= left;j< right+ 1;++j)
  12. printf( "%5d",p[j]);
  13. printf( "\n");
  14. return ;
  15. }

对于插入排序优化的思考:

对于插入排序,最差情况下的时间复杂度为O(N²),若寻找插入点的位置时,我们不使用线性的依次寻找,而使用二分法,二分法查找的时间复杂度为O(lgN)(这里的lg是以2为底的)。这样,可以将插入排序的时间复杂度将为O(N*lgN)。这样是否合理呢?

答案是不能,因为找到插入点后,仍需要将插入点后面的元素向后移一位,这个过程的时间复杂度是O(N),这样的话,二分法查找插入点其实是多余的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值