编辑文章 - 博客频道 - CSDN.NET

来源:互联网 发布:鸣人和佐助 知乎 编辑:程序博客网 时间:2024/05/23 11:55

</pre>《编程珠玑》进入了第三部分。前两部分说实在的,我是看的有些云里雾里,可能因为是学生的缘故吧,没有什么实际的工作经验,体会不到作者写的精髓,第三部分可不一样,大致看了一下,倍感亲切,下面会对每部分做一篇小小的总结,希望对大家有所帮助.<p></p><p>  插入排序:</p><p>           作者上来先举了一个纸牌游戏的例子,当我们拿到一张牌的时候会不自觉的将这张牌按照大小的顺序放在相应的位置上,想想我们是怎么放的?一般就是从最小的开始找,找到第一个大于该牌的位置,然后放在其后面就行了。其实这里面用到的就是插入排序。</p><p>           用计算机语言描述就是:数组x[0....n-1]中x[0...m]已经按照从小到大排好序,现在将x[m+1]放置在正确的位置上面,可以这样做:将x[m]与该数进行比较(注意这里我没有说x[m+1])如果x[m]<=该数的话,因为x[0..m]本来就是排序的,所以这个时候根本不需要再进行排序,直接进行下一个元素的排序进行了,当x[m]>该数的时候,将x[m]往右移动,因为这个时候x[m]是包括该数在内的最大的数,理应在排好序之后出现在x[m+1]的位置上面,下面的工作就是重复上面的比较,只不过这个时候需要用这个数与x[m-1]进行比较而已,比较一直到最左边或者是找到合适的插入位置即可。</p><p>        代码片段:</p><pre name="code" class="cpp">void   insertsort(int *a,int  amount){       if(a==nullptr||amount<=0)            return ;       int  i,j,t;       for(i=1;i<amount;i++)       {            t=a[i];            for(j=i;j>0&&a[j-1]>t;j--)                    a[j]=a[j-1];            a[j]=t;        }}上述的代码片段的确很简单,但是千万不要忘了边界检验的问题,因为我们在面试的时候面试官是很看重这个的.虽然我们平常无所谓,但是在面试的时候一定要注意这些细节的东西才行。
本科学过算法课的都知道,插入排序的时间复杂度是O(n*n)但是从上面的代码段中我们也可以看出来,插入排序的运行时间是和数组本来的元素是否大致上有序有着重要的关系,一个有序的数组根本不会发生元素的移动,这个时候在O(n)内可以完成排序(虽然实际上什么也没干)
说到插入排序,不得不说到和插入排序类似的希尔排序:
    希尔排序又称为增量递减的排序,这种排序算法类似于插入排序,但是不同的地方在于每次比较的时候我们不是直接利用a[j]与a[j+1]进行比较,而是有着一定的间隔,这个间隔一开始很大,会按照某种我们自己预先设定好的规则逐渐的递减知道最后的1,这个时候也就成了插入排序,那小伙伴们会说既然最后成了插入排序,那么为什么不直接就用插入排序呢?上面不是说了么?当数组的元素存在着部分有序的时候,移动的元素数目会减少啊,所以我个人觉得希尔排序的除了间隔为1之外的部分,都是为了最后的插入排序做准备的,目的就是使其存在某种有序性,减少最后的移动次数。
快速排序:
    C语言本身提供了一个qsort函数,来实现快速排序,这个函数提供了四个参数.这里不再说明了,网上直接问问度娘就行了。
    《编程珠玑》上面提到了多个版本的快速排序的算法,那么下面先来看看什么叫做快速排序。
     快速排序是将待排序数组中的某个元素作为基准,将该部分待排序的元素分为两部分,左侧的元素都小于该基准,而右侧的元素都大于等于该基准,虽然这个时候只有这个基准找到了自己正确的位置,但是我们现在知道的信息还有别的啊,没错,就是上面说的左侧的都比这个基准小,右侧的都比这个基准大,然后我们再利用递归的算法,分别对这两部分进行排序就可以了。
     为什么快速排序能够对数组元素进行正确的排序呢?
     看过《算法导论》的都知道,这个可以用归纳的方法来证明,这里就不再啰嗦了,《编程珠玑》上面也有。
     下面是代码片段:
     <pre name="code" class="cpp">void qsort(int* a,int L,int R){      if(L>=R) return ;int  m=L;int  t=a[m];int   tmp;for(int i=L+1;i<=R;i++)if(a[i]<t){m++;t=a[m];a[m]=a[i];a[i]=t;}         tmp=a[L];a[L]=a[m];a[m]=tmp;        qsort(a,L,m-1);        qsort(a,m+1,R);}
上面的代码片段能正常的工作,但是对于一些特殊的输入,会存在着很严重的问题,比如说数组本身就是个有序的数组,这个时候如果我们选择最左边的元素作为基准的话,很显然这个时候在递归的时候左侧部分会立即返回,而右侧部分还剩下n-1个元素,这个时候排序算法的时间复杂度为O(n*n),
 额,忘了,跑偏了,作者是通过当数组元素个数相同的时候引入双向划分的,我上面采用的是有序的数组来说明快速排序对非随机输入的数组的性能。
 回到正题:
        双向划分:什么是双向划分,从上面的算法中我们知道待排序的元素是从最左侧一直到最有右侧的,这种酒叫做单向的,所以双向的就是在左右两侧同时开工,这个时候算法的不变量成了(假设左侧为x[i],右侧的为x[j]),x[Lbegin,i-1]之间的元素均小于等于基准,x[j+1,Rend]之间的元素均大于等于基准,当x[i]与x[j]均不满足上述的不变量时,就需要交换两者的值,直到两者出现交叉为止,当交叉的时候满足x[i]>=t同时x[j]<=t(下面的代码片段说明为什么)这个时候因为i>j,若仍不停止的话,x[i]会重读刚才x[j]z走过的路线,而x[j]则会重复刚才x[i]做过的路线,明显是个错误。
 下面是代码段:
</pre><pre name="code" class="cpp">
</pre><pre name="code" class="cpp">void  qsort(int* a,int  Lbegin,int Rend){if(Lbegin>=Rend) return ;int  i=Lbegin;int  j=Rend+1;int  t=a[Lbegin];int  temp;while(true){do{         i++;}while(i<=Rend&&a[i]<t);do{         j--;}while(a[j]>t);if(i>j)break;temp=a[i];a[i]=a[j];a[j]=temp;}temp=a[Lbegin];t=a[j];a[j]=temp;}


0 0
原创粉丝点击