希尔排序,基数排序,归并排序

希尔排序:

将无序数组分割为若干个子序列,子序列不是逐段分割的,而是相隔特定的增量的子序列,对各个子序列进行插入排序;然后再选择一个更小的增量,再将数组分割为多个子序列进行排序......最后选择增量为1,即使用直接插入排序,使最终数组成为有序。(希尔排序可以作为插入排序的一种。)

不稳定的排序算法,时间复杂度O(n^1.5),最好时间复杂度O(n),最坏时间复杂度O(n^2),辅助内存O(1)

基本思想:希尔排序把n个元素按一定的间隔分成几组,然后按组为单位进行插入排序。 。

   将待排记录序列以一定的增量间隔h 分割成多个子序列,对每个子序列分别进行一趟直接插入排序, 然后逐步减小分组的步长h ,对于每一个步长h 下的各个子序列进行同样方法的排序,直到步长为1 时再进行一次整体插入排序。

  因为不管记录序列多么庞大,关键字多么混乱,在先前较大的分组步长h下每个子序列的规模都不大,用直接插入排序效率都较高。 尽管在随后的步长h递减分组中子序列越来越大,但由于整个序列的有序性也越来越明显,则排序效率依然较高。这种改进抓住了直接插入排序的两点本质,大大提高了它的时间效率。


例子说明:

增量的选择:在每趟的排序过程都有一个增量,至少满足一个规则 增量关系 d[1] > d[2] > d[3] >..> d[t] = 1 (t趟排序);根据增量序列的选取其时间复杂度也会有变化,这个不少论文进行了研究,在此处就不再深究; 本文采用首选增量为n/2,以此递推,每次增量为原先的1/2,直到增量为1;
下图详细讲解了一次希尔排序的过程:


希尔排序代码:
//希尔排序
void shellSortCore(int *array,int increment,int length)
{
 int j,temp;
 for(int i=increment;i<length;i+=increment)
 {
  j=i-increment;
  temp=array[i];
  //进行一次插入排序
  while(j>=0&&array[j]>temp)
  {
   array[j+increment]=array[j];
   j=j-increment;
  }
  array[j+increment]=temp;
 }
// for(int i=0;i<length;i++)
//  cout<<array[i]<<ends;
// cout<<endl;
}
void shellSort(int *array, int length)
{
 int increment=length/2;//初始增量大小
 while(increment>=1)
 {
  shellSortCore(array,increment,length);
  increment/=2;
 }
}

基数排序:
基本思路: 基数排序(以整形为例),将整形10进制按每位拆分,然后从低位到高位依次比较各个位。主要分为两个过程:
(1)分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中(比如53,个位为3,则放入3号桶中)
(2)收集,再将放置在0~9号桶中的数据按顺序放到数组中
重复(1)(2)过程,从个位到最高位(比如32位无符号整形最大数4294967296,最高位10位)
以【521 310 72 373 15 546 385 856 187 147】序列为例,具体细节如下图所示:



在数据中最高位为3,进行了三次分配、收集过程后,变成有序数组。

特点:稳定排序。

归并排序

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

假设我们有一个没有排好序的序列,那么首先我们使用分割的办法将这个序列分割成一个一个已经排好序的子序列,然后再利用归并的方法将一个个的子序列合并成排序好的序列。分割和归并的过程可以看下面的图例。


从上图可以看出,我们首先把一个未排序的序列从中间分割成2部分,再把2部分分成4部分,依次分割下去,直到分割成一个一个的数据,再把这些数据两两归并到一起,使之有序,不停的归并,最后成为一个排好序的序列。

如何把两个已经排序好的子序列归并成一个排好序的序列呢?可以参看下面的方法。

假设我们有两个已经排序好的子序列。

序列A:1 23 34 65

序列B:2 13 14 87

那么可以按照下面的步骤将它们归并到一个序列中。

(1)首先设定一个新的数列C[8]。
(2)A[0]和B[0]比较,A[0] = 1,B[0] = 2,A[0] < B[0],那么C[0] = 1
(3)A[1]和B[0]比较,A[1] = 23,B[0] = 2,A[1] > B[0],那么C[1] = 2
(4)A[1]和B[1]比较,A[1] = 23,B[1] = 13,A[1] > B[1],那么C[2] = 13
(5)A[1]和B[2]比较,A[1] = 23,B[2] = 14,A[1] > B[2],那么C[3] = 14
(6)A[1]和B[3]比较,A[1] = 23,B[3] = 87,A[1] < B[3],那么C[4] = 23
(7)A[2]和B[3]比较,A[2] = 34,B[3] = 87,A[2] < B[3],那么C[5] = 34
(8)A[3]和B[3]比较,A[3] = 65,B[3] = 87,A[3] < B[3],那么C[6] = 65
(9)最后将B[3]复制到C中,那么C[7] = 87。归并完成。 

C代码:

//归并排序中的合并算法

void Merge(int‍ array[], int start, int‍ mid, int end)

{

      int temp1[10], temp2[10];

      int n1, n2;

      n1 = mid - start + 1;

      n2 = end - mid;

   //拷贝前半部分数组

      for (int i = 0; i < n1; i++)

             temp1[i] = array[start + i];

   //拷贝后半部分数组

      for (int‍ i = 0; i < n2; i++)

             temp2[i] = array[mid + i + 1];

//把后面的元素设置的很大

      temp1[n1] = temp2[n2] = 1000;

   //逐个扫描两部分数组然后放到相应的位置去

      for (int‍ k = start, i = 0, j = 0; k <= end; k++)

      {

             if (temp1[i] <= temp2[j])

             {

                    array[k] = temp1[i];

                    i++;

             }

             else

             {

                    array[k] = temp2[j];

                    j++;

             }

      }

}

//归并排序

void MergeSort(int array[], int‍ start, int‍ end)

{

      if (start < end)

      {

             int i;

             i = (end + start) / 2;

          //对前半部分进行排序

             MergeSort(array, start, i);

             //对后半部分进行排序

             MergeSort(array, i + 1, end);

             //合并前后两部分

             Merge(array, start, i, end);

      }

}

    特点:归并排序是稳定的,它的最差,平均,最好时间都是O(nlogn)。但是它需要额外的存储空间,这在某些内存紧张的机器上会受到限制。

    归并算法是又分割和归并两部分组成的。对于分割部分,如果我们使用二分查找的话,时间是O(logn),在最后归并的时候,时间是O(n),所以总的时间是O(nlogn)。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值