[C描述算法入门]冒泡排序和直接选择排序

614 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

前言

        冒泡排序和直接选择排序属于常见排序,本文就来简单分享一波笔者的学习经验与心得。

        笔者水平有限,难免存在纰漏,欢迎指正交流。

冒泡排序

        冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

算法步骤

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

img

代码实现

        冒泡排序还有一个小优化,就是立一个 flag,如果在一趟序列遍历中元素没有发生交换,则证明该序列已经有序,就不需要再进行排序了,虽然这种改进对于提升性能来说并没有什么太大作用。

 void Swap(int* x, int* y)
 {
     int tmp = *x;
     *x = *y;
     *y = tmp;
 }
 ​
 void BubbleSort(int* arr, int sz)
 {
     assert(arr);
     
     for(int j = 0; j < sz - 1; ++j)
     {
         int flag = 0;
         for(int i = 0; i < sz - 1 - j; ++i)
         {
             if(arr[i] > arr[i + 1])
             {
                 Swap(&arr[i], &arr[i + 1]);
                 flag = 1;
             }
         }
         if(flag == 0)
             break;        
     }
 ​
 }

什么时候最快

        当输入的数据已经是正序时(都已经是正序了,我还要你冒泡排序有何用啊)。

什么时候最慢

        当输入的数据是反序时(写一个 for 循环反序输出数据不就行了,干嘛要用你冒泡排序呢,我是闲的吗)。

        所以冒泡排序挺废的...

冒泡排序的特性总结

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(n2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

直接选择排序

思路与实现

        将序列分为已排序序列和待排序序列,每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在已排序序列的末尾,直到全部待排序的数据元素排完 。

img

步骤

  1. 首先在未排序序列中找到最小(大)元素,存放到已排序序列的起始位置。
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。
 void SelectSort(int* arr, int sz)
 {
     assert(arr);//检测指针是否为空,不允许传入NULL
     
     for(int i = 0; i < sz - 1; ++i)
     {
         int min_i = i;
         for(int j = i + 1; j < sz; ++j)
         {
             if(arr[j] < arr[min_i])
                 min_i = j;
         }
         Swap(&arr[i], &arr[min_i]);       
     }
 ​
 }

我们来搞一个稍微优化一丢丢的版本

        上面那个思路是每次找出最小的放到已排序的序列中,这里考虑每次不仅找出最小的,还要把最大的找出来,假如我们要排升序,那就把最小的放到序列前面,最大的放到序列后面,然后让序列向中间缩小,直到范围左右边界相遇或相错时停止。

 void SelectSortImp(int* arr, int sz)
 {
     assert(arr);
 ​
     int left = 0;
     int right = sz - 1;
 ​
     while (left < right)
     {
         int maxi = right;
         int mini = left;
         for (int i = left; i <= right; ++i)
         {
             if (arr[i] > arr[maxi])
                 maxi = i;
             if (arr[i] < arr[mini])
                 mini = i;
         }
 ​
         Swap(&arr[left], &arr[mini]);
         Swap(&arr[right], &arr[maxi]);
         ++left;
         --right;
     }
 ​
 }

        这样就完成了吗?

        不,实际上还有一个细节没有考虑到:如果下标left和maxi重合怎么办?

        第一个Swap函数一交换就把mini对应的最小值给换到left位置上去了,这样一来maxi对应的值就不是最大值而是最小值了!!

        所以我们要在下一次Swap函数交换前把maxi修正一下:maxi = mini

 void SelectSortImp(int* arr, int sz)
 {
     assert(arr);
 ​
     int left = 0;
     int right = sz - 1;
 ​
     while (left < right)
     {
         int maxi = right;
         int mini = left;
         for (int i = left; i <= right; ++i)
         {
             if (arr[i] > arr[maxi])
                 maxi = i;
             if (arr[i] < arr[mini])
                 mini = i;
         }
 ​
         Swap(&arr[left], &arr[mini]);
         if (left == maxi)
             maxi = mini;
         Swap(&arr[right], &arr[maxi]);
         ++left;
         --right;
     }
 ​
 }

直接选择排序的特性总结

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用。
  2. 时间复杂度:O(n2)。数据顺序对算法性能没有影响,无论什么数据进去都是 O(n²) 的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧(最差排序之一)。
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~

src=http___c-ssl.duitang.com_uploads_item_201708_07_20170807082850_kGsQF.thumb.400_0.gif&refer=http___c-ssl.duitang.gif