二路归并排序

来源:互联网 发布:刚哥哥淘宝店招制作 编辑:程序博客网 时间:2024/05/01 17:45
一、算法思想

二路归并是指把两个有序序列合并成一个新的有序序列的过程。方法:新建一个序

列,其长度等于要合并的两个序列的长度之和。我们称要合并的序列为A和B,新建的为C。然后设置3个指针:t1、t2、t3,分别指向A、B、C的第一个元素。比较t1,t2指向元素的大小,将较小的存储在C中t3所指的位置,并将指向较小元素的指针和t3都向后移一位。重复上述操作,直到A或B序列已经全部存入C中。最后把另一序列的剩余部分赋值到C序列的后半部分。此时C就是一个有序序列。

二路归并排序的思想:采用递归的方法把原序列分割成两个子序列,再把子序列也分成两个子序列,直到两个子序列的长度都为一时,进行二路归并,使这个子序列变为有序序  列。再把已得到的有序子序列进行合并。最终会使整个序列变为有序序列。

举例:(括号内的是有序序列)

初始序列           [51]   [33]  [62]  [96]  [87]  [17]  [28]

一趟二路归并排序后 [33    51]   [62   96]  [17   87]  [28]

二趟二路归并排序后 [33    51    62    96]  [17   28   87]

三趟二路归并排序后 [17   28   33   51   62   87   96]

二、过程

procedure merge(p,q:integer);

var s,e,t1,t2,f,k:integer;

begin

  if q=p then exit;

  f:=(p+q) div 2;

  merge(p,f);

  merge(f+1,q);

  fillchar(b,sizeof(b),0);

  t1:=p; t2:=f+1;

  k:=p-1;

  while (t1<=f) and (t2<=q) do begin

    inc(k);

    if a[t1]<=a[t2] then begin {“<=”是保持算法稳定性的关键}

      b[k]:=a[t1];

      inc(t1);

    end

                    else begin

      b[k]:=a[t2];

      inc(t2);

    end;

  end;

  e:=0;

  if t1<=f then begin s:=t1; e:=f; end;

  if t2<=q then begin s:=t2; e:=q; end;

  for i:=s to e do b[i+k-s+1]:=a[i];

  for i:=p to q do a[i]:=b[i];

end;

三、评价

归并排序的时间复杂度是O(nlog2n)。归并排序是一种稳定的排序算法。


---------------------------------------------------

归并排序的主要思想是:把待排序的记录序列分成若干个字序列,先将每个子序列的记录排序,再将已排序的子序列合并,得到完全排序的记录序列。归并排序可分为多路归并排序和二路归并排序。


二路归并排序算法思路是:对任意长度为n的序列,首先看成是n个长度为1的有序序列,然后两两归并为n/2个有序表;再对n/2个有序表两两归并,直到得到一长度为n的有序表。

具体的算法实现如下:

把两个有序表归并成一个有序表:

void Merge(RecordType a[],RecordType b[],int i,int m,int n)

/* 将有序表a[i..m]以及a[m+1..n]有序归并到b[i..n]中 */

{

int la,lb,lc;

la=i;lb=m+1;lc=i; /*序列la,lb,lc的始点*/

while(la<=m&&lb<=n)

{

if(a[la].key<a[lb].key)

b[lc++]=a[la++];/*有序合并*/

else

b[lc++]=a[lb++];

}

while(la<=m)

b[lc++]=a[la++];/*复制第一个序列中剩下的元素*/

while(lb<=n)

b[lc++]=a[lb++];/*复制第二个序列中剩下的元素*/

}

一趟归并排序算法:

void MergePass(RecordType R[],RecordType A[],int n,int c)

/*把序列R[0..n-1]以长度为c的序列进行两两归并,归并后存在序列A中*/

{

int i=0,j;

while(i+2*c-1<=n-1) /*长度均为c的两个子序列合并为一个序列*/

{

Merge(R,A,i,i+c-1,i+2*c-1);

i+=2*c;

}

if(i+c-1<n) /*长度不等的两个子序列合并为一个序列*/

{

Merge(R,A,i,i+c-1,n-1);

}

else

for(j=i;j<=n-1;j++) /*仅剩一个序列时,直接复制到A中*/

A[j]=R[j];

}

归并排序算法:

void MergeSort(RecordType R[],int n)

/*对表r中的第0到第n-1个记录进行归并排序*/

{

int c=1; /*每次归并的长度,初始为1*/

RecordType A[MAXSIZE]; /*需要一个辅助空间*/

while(c<n)

{

MergePass(R,A,n,c);/*一次合并,结果存入A中*/

c*=2; /*序列长度扩大一倍*/

MergePass(A,R,n,c);/*再次合并,结果存入R中*/

    c*=2;

}

}

归并排序的时间复杂度为O(nlog2n),利用二路归并排序时,需要利用与待排序序列长度相同的数组作为临时存储单元,故该排序方法的空间复杂度O(n)。
由于二路归并排序中,每两个有序子序列合并成一个有序序列时,若分别在两个有序子序列中出现有相同关键字的记录,则会使前一个有序子序列中相同关键字的记录先复制,后一个有序子序列中相同关键字的记录后复制,从而保持了他们的相对位置不变,因此,二路归并排序是一种稳定的排序方法。

0 0
原创粉丝点击