排序算法

 (原创文章,转载请注明出处!)

一、插入排序

要点就是保持扫描过的元素是有序的,使之成为一个有序的元素序列(升序或降序)

每次取一个新的元素,扫描已排序的元素序列,找到其合适的位置,将新元素插入。

实例:打扑克时,揭牌保持扑克牌的顺序。

时间复杂度:O(N2)

算法的实现:使用能缓存一个元素的辅助存储空间。从0到N-1来扫描初始元素列表,从i到0扫描已经排序的元素,寻找i+1元素的合适位置。将第i+1元素存到辅助存储空间,将第i个元素移动到位置i+1,将第i-1个元素移动到位置i,以此类推,直到将第i+1个元素的合适位置空出来,然后将第i+1个元素放到合适的位置。

 

二、Shell排序

插入排序在序列基本有序时效率较高,并且在序列规模不是很大时效率也很高。Shell排序就是从这些方面对插入排序算法的改进。

shell排序先根据较长的步长对序列分组,进行排序,然后逐步减少步长再排序,直到步长为1为止。

初始时使用的步长大,每组的元素少,效率较高;

逐步的使用的步长小,每组的元素多了,但也基本有序了,效率依然较高。

例子: 12, 4,26,19,14,8,24,6,有8个元素。

第一轮:步长使用8/2=4,那么分成4组子序列进行排序,(1,5),(2,6),(3,7),(4,8)

            序号1、5位置上分别是12和14, 12<14,不用动

            序号2、6位置上分别是4和8, 4<8,不用动

            序号3、7位置上分别是26和24, 26>24,交换

            序号4、8位置上分别是19和6, 19>6,交换

      序列变成:12   4   24    6    14     8     26    19

第二轮:步长使用4/2=2,那么分成2组子序列进行排序:(1,3,5,7),(2,4,6,8)

           序号1,3,5,7位置上分别是12, 24 , 14 , 26 , 使用插入排序排完序,变成:12 , 14, 24, 26 (只有24与14进行一次交换)

           序号2,4,6,8位置上分别是4,  6,  8,  19,(没有交换)

      序列变成:12   4   14   6    24    8    26     19

第三轮:步长使用2/2=1,那么分成1组子序列进行排序,即整个序列:12   4   14   6    24    8    26     19

            插入排序过程:4  12   14   6    24    8    26     19

                                4   6    12   14   24    8    26    19

                                4   6     8    12   14   24   26    19

                                4   6     8    12   14   19   24    26

结束。

 

三、快速排序

分治思想的应用。

时间复杂度:O(N(logN))

用所需要排序的序列中的某个元素p(比如:第一个)将序列一分为二:左边的元素都小于等于元素p,右边的都大于等于元素p;然后分别对左、右子序列进行相同划分;

最后将左、右子序列合并,则得到排好序的序列。

如何使用元素p来划分待排序序列?假设元素p是第一个元素(最左边的元素)。

策略一:元素P存到一个tmp,那么p的位置空出来了,记为vac;

           从右(最右边的一个元素)往左,寻找比p小的元素,找到就放到vac位置,找到的比p小的元素的位置就空出来了,记为vac,新的最右边记为:H=vac-1;

           从左边第二个元素开始往右,寻找比p大的元素,找到就放到vac位置,找到的比p大的元素的位置就空出来了,记为vac,新的最左边记为:L=vac+1;

           再从H、L位置分别重复步骤二、三,直到H遇到L。

           将元素p存到位置vac。

策略二:将元素p的位置记为M,从左(第二个元素)往右扫描序列,扫描到的位置记为I,

            如果扫描到的元素比p小,就M=M+1,交换位置M与位置I的元素。

            继续往右扫描,重复步骤二,直到全部扫描完。

  

四、混和排序

利用快速排序先进行大致的排序,比如:L > 15,15个元素内部无序的,但每15个、15个之间是有序的。

再利用插入排序来进行最后的,所有元素排序。

优点:插入排序在元素基本有序的情况下,效率很高。

 

 

五、堆排序

堆:一颗二叉树,具有如下的特点,

------父元素,比左右孩子都小。根元素是最小的元素。

------只有最后两层可能有叶子节点。节点在每一层中,从左往右排列。

堆的存储:使用数组。将元素按层,从左至右依次保存。元素Xi的父节点是Xi/2;元素Xi的左孩子X2i,右孩子X2i+1

堆的操作:

------删除最小的元素,即根元素:

  1. 将根元素与最后一个元素交换
  2. 然后再将新根元素往下移:
    • 往下移的结束条件:元素处于叶子节点的位置,或者,元素小于或等于其左右子节点
    • 选择较小的子节点,然后将元素与这个较小的子节点交换
  3. 重复迭代步骤2,直到把新根元素移到合适的位置。

      删除根元素,也可以直接使用将最后一个元素往上移动的方式来调整堆。

------插入一个新元素:

  1. 将新元素加入到数组中的最后一个堆元素后面,
  2. 将新元素与其父元素比较,如果新元素小,则交换新元素与其父元素的位置。(这是将元素往上移动的操作)
  3. 重复迭代步骤2,直到把新元素交换到正确的位置

 ------将根元素用新元素替代

  1. 直接使用删除操作中的步骤2、3即可

 

利用堆进行排序:

扫描数组,将每个元素逐一插入到堆中,再逐一:输出堆的根元素,并删除它,堆调整。

注意要不使用另外一个数组作为辅助存储空间。堆的构建在原数组进行。

从i=1开始扫描数组,<i的元素已经是堆了,将元素i加入,调成<=i的数组中的元素,使之保持堆属性。

 

六、归并排序

归并排序是对分而治之思想的应用。将数组一分为二,分别对两个子数组排序,然后将两个排好序的子数组进行合并,得到最后排好序的数组。

对子数组的排序也是利用上述的分治的方法,所以归并排序是一个递归的过程。合并两个长度为n的子数组的时间复杂度是O(n),并且需要O(2n)的辅助空间来保存合并好的结果。算法的实现。还是使用上述的例子:

 12, 4,26,19,14,8,24,6  。 8 个数组成的数列。

step1 : 分成两个子数组s1:12, 4,26,19     和    s2:14,8,24,6 ,分别对这两个子数组来进行归并排序

对s1:  分成两个子数组s11:12, 4   和   s12:26,19

对s11 使用归并排序,将12与4通过合并操作,得到排好序的数列 4 , 12

对s12 使用归并排序,将26与19通过合并操作,得到排好序的数列 19 , 26

对s11与s12使用合并操作,得到排好序的数列4 , 12, 19 , 26,则得到s1排好序的结果。

对s2进行相同的处理过程后,得到排好序的数列6, 8, 14, 24

然后,对s1与s2进行合并操作,得到整个数列最终的排序结果:4, 6, 8, 12, 14, 19, 24, 26  。

算法的实现有两个函数:mergeSort(), merge()

调用过程如下:

mergeSort( s1 )                       

 
(入栈)mergeSort(s1)

mergeSort( s11 )

 
(入栈)mergeSort(s11)
mergeSort(s1)

mergeSort(12)

mergeSort(4)

merge(12, 4)

 
mergeSort(s11)(出栈)
mergeSort(s1)

mergeSort( s12 )

 
(入栈)mergeSort( s12 )
mergeSort( s1)

mergeSort(26)

mergeSort(19)

merge(26, 19)

 
mergeSort( s12 ) (出栈)
mergeSort( s1)

merge(s11, s12)

 
mergeSort( s1) (出栈)

mergeSort(s2)

 
(入栈)mergeSort( s2)

........

merge(s1, s2)   /* 得到最终排序的结果 */

 

posted @ 2014-08-07 21:51  activeshj  阅读(166)  评论(0编辑  收藏  举报