排序算法 总结&思考(一)

--前言

排序算法是最基本而且最基础的问题,特别是在C语言中。在C++,JAVA等面向对象设计中通常会提供相应的库函数,使得编程者不必要考虑详细的设计处理,调用即可。即便如此,这一问题仍然是面试笔试中必然涉及到的话题。写一篇自己对各种排序算法的认识,作为以后找工作的基础吧


1.简单易懂 の 直接插入排序(InsertSort)

谈到排序,日常接触最多的例子就是扑克牌的顺序调整,因为分配到的是洗匀的卡牌,所以需要对此进行排序,方便后续出牌

(这里只考虑按int大小排序,至于实际扑克点数大小,个人打牌习惯,不在范围之内~~)

我们一般会从左至右,依次把每一张非顺序的牌放在左边正确的位置,比如5会插在4和6之间,依此进行至结束。这种方式最直观最易理解,操作也最方便

算法来源于现实,而高于现实--计算机中实际应用往往比现实模型要复杂

一般使用数组进行排序,那么我们的“插入操作”就不仅仅是把卡牌插在两张卡的缝隙之间这么简单。数组是固定的,要在中间插入一个数据需要把后面所有数字平移一位!这是插入排序速度的瓶颈

这一瓶颈使得在平均情况和最坏情况下,时间复杂度都是O(n²)。值得高兴的是,在最好的情况下(已经排好序),时间复杂度为O(n),所以在基本有序的序列中,使用直接插入排序还是比较有效的

直接插入排序的伪代码:

for i = [1,n)
  t = x[i]
  for(j=i; j>0 && x[j-1]>t; j--)
    x[j] = x[j-1]
  x[j] = t

2. 创造有序 の 希尔排序(ShellSort)

基于直接插入排序在基本有序的情况下表现较好,一种改进的插入排序算法--希尔排序诞生。原理是先通过数次小集合的顺序调整,使得最后整个序列排序时达到基本有序的状态,减少移动元素的开销

D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成.

一般的初次取序列的一半为增量,以后每次减半,直到增量为1。关于增量算法的选择请参考更多资料

希尔排序由于增量选取和控制方式的不同,代码实现也不尽相同。下面是一个简单的C语言实现:
     const int n = 5;
     int i, j, temp; 
     int gap = 0;
     int a[] = {5, 4, 3, 2, 1}; 
     while (gap<=n)
     {
          gap = gap * 3 + 1;
     } 
     while (gap > 0) 
     {
         for ( i = gap; i < n; i++ )
         {
             j = i - gap;
             temp = a[i];             
             while (( j >= 0 ) && ( a[j] > temp ))
             {
                 a[j + gap] = a[j];
                 j = j - gap;
             }
             a[j + gap] = temp;
         }
         gap = ( gap - 1 ) / 3;
     }

这种改进使得希尔排序时间复杂度可以达到O(nlogn)-O(n²)之间。根据增量的不同,最佳平均情况时间复杂度可以达到O(n^1.3),最好和最坏情况同直接插入排序(O(n),O(n²)),在这里只提及这种思想即止(平常应用中大量采用快排,shell排序因为流程控制复杂些,比较少见)

3  一目十行 の 简单选择排序(SelectSort)

有趣的是,逻辑上“简单”一词在现实中和计算机中的影响大不一样。生活中,我们一般会选择“怎么简单怎么做”,还是以扑克牌为例子,有些人会从手中数量不多的牌中,从小到大依次选择并取出牌,放置在新的位置进行排序。而在计算机中,这一逻辑非常清晰简单的算法却被评价为效率最低的算法

原因很简单:思维方式&应用场景决定了这点。计算机没有"一目十行"的能力,无法从中快速挑出点数最小的牌,并且,牌的总数量一般比较少,如果要从上百张牌中一个个挑选,恐怕超人才有这种速度吧~

void SelectSort(SqList *L)
{
 int i,j,min;
 for(i=1;i<L->length;i++)
 { 
  min = i;       
  for (j = i+1;j<=L->length;j++) 
  {
       if (L->r[min]>L->r[j]) 
             min = j;   
  }
  if(i!=min)    
       swap(L,i,min);
 }
}
因为每一个元素的选择都需要扫描剩下的所有元素,这种比较导致简单选择排序在 任何情况下时间复杂度都是O(n²),运行起来可就不“简单”了

4  快速稳定 の 堆排序(HeapSort)

堆排序已经在前面一篇文章中介绍过了,所以这里就不再多言。堆排序也是基于“选出”最小元素的选择排序类,只是因为它建立了独有的数据结构,使得每次操作的开销平均到O(logn)级别,是一个较优的排序算法

需要注意的是,标题中的“稳定”并不是指的通常说称排序算法的稳定性,而是说堆排序在任何情况下时间复杂度都达到O(nlogn)。排序算法的稳定性也很好理解,相同元素在排序过程中是否会保证顺序不变(某些特殊情况可能会用到这点),如果是,则称算法是稳定的。除直接插入排序外,其他三个算法都是不稳定的


插入排序类和选择排序类思想比较简单,后面将介绍另4种应用较广的其他类算法

(未完待续)

 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值