归并排序

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

目 录

1归并排序

2归并操作

3算法描述

4比较

5用途

5.1 排序

5.2 求逆序对数

6示例代码

6.1 归并排序

6.2 C语言

6.3 Pascal 语言

6.4 Basic语言

1归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。值得注意的是归并排序是一种稳定的排序方法。

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

2归并操作

归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。

如设有数列{6,202,100,301,38,8,1}

初始状态:6,202,100,301,38,8,1

第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;

第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;

第三次归并后:{1,6,8,38,100,202,301},比较次数:4;

总的比较次数(逆序数)为:3+4+4=11;

3算法描述

归并操作的工作原理如下:

第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置

第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

重复步骤3直到某一指针超出序列尾

将另一序列剩下的所有元素直接复制到合并序列尾

4比较

归并排序是稳定的排序.即相等的元素的顺序不会改变.如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时输出的 1(1) 2(3) 2(4) 3(2) 5(5) 中的2 和 2 是按输入的顺序.这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要.这也是它比快速排序优势的地方.

5用途

排序

(速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列,应用见2011年普及复赛第3题“瑞士轮”的标程)

求逆序对数

具体思路是,在归并的过程中计算每个小区间的逆序对数,进而计算出大区间的逆序对数(也可以用树状数组来求解)

6示例代码

归并排序

归并排序具体工作原理如下(假设序列共有n个元素):

将序列每相邻两个数字进行归并操作(merge),形成floor(n/2)个序列,排序后每个序列包含两个元素

将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素

重复步骤2,直到所有元素排序完毕

示例代码

C语言

输入参数中,需要排序的数组为array[],起始索引为first,终止索引为last。调用完成后,array[]中从first到last处于升序排列。

int is1[n],is2[n];// 原数组is1,临时空间数组is2,n为个人指定长度
void mergeSort(int a,int b)// 下标,例如数组int is[5],全部排序的调用为mergeSort(0,4)
{
    if (a<b)
    {
        int mid=(a+b)/2;
        mergeSort(a,mid);
        mergeSort(mid+1,b);
        merge(a,mid,b);
    }
}
void merge(int low,int mid,int high)
{
    int i=low,j=mid+1,k=low;
    while (i<=mid&&j<=high)
        if (is1[i]<=is1[j]) // 此处为排序顺序的关键,用小于表示从小到大排序
            is2[k++]=is1[i++];
        else
            is2[k++]=is1[j++];
    while (i<=mid)
        is2[k++]=is1[i++];
    while (j<=high)
        is2[k++]=is1[j++];
    for (i=low;i<=high;i++)// 写回原数组
        is1[i]=is2[i];
}

Pascal 语言

1. ```delphi type arrtype=array[1..500]of integer; procedure MergeSort(arr:arrtype;first,last:integer); var mid:integer; begin mid:=0; if first<last then begin mid = (first+last)div 2; MergeSort(arr, first, mid); MergeSort(arr, mid+1,last); Merge(arr,first,mid,last); end; end;

2. ```delphi
program gbpx;
const maxn=7;
type arr=array[1..maxn] of integer;
var a,b,c:arr;
i:integer;
procedure merge(r:arr;l,m,n:integer;var r2:arr);
var i,j,k,p:integer;
begin
i:=l;j:=m+1;k:=l-1;
while (i<=m)and (j<=n) do
begin
k:=k+1;
if r[i]<=r[j] then begin r2[k]:=r[i];i:=i+1 end
else begin r2[k]:=r[j];j:=j+1 end
end;
if i<=m then
for p:=i to m do begin k:=k+1;r2[k]:=r[p] end;
if j<=n then
for p:=j to n do begin k:=k+1;r2[k]:=r[p] end;
end;
procedure mergesort( var r,r1:arr;s,t:integer);
var k:integer; c:arr;
begin
if s=t then r1[s]:=r[s] else
begin
k:=(s+t) div 2;
mergesort(r,c,s,k);
mergesort(r,c,k+1,t);
merge(c,s,k,t,r1)
end;
end;
begin
write('Enter data:');
for i:= 1 to maxn do
read(a[i]);
mergesort(a,b,1,maxn);
for i:=1 to maxn do
write(b[i]:9);
writeln;
end.

3

const
max=100000;
var
a,r:array[1..max] of longint;
n,i:longint;
procedure msort(s,t:longint);
var
m,i,j,k:longint;
begin
if s=t then exit;
m:=(s+t) div 2;
msort(s,m);
msort(m+1,t);
i:=s;
j:=m+1;
k:=s;
while (i<=m) and (j<=t) do
begin
if a[i]<a[j] then
begin
r[k]:=a[i];
inc(i);
inc(k);
end
else
begin
r[k]:=a[j];
inc(j);
inc(k);
end;
end;
while i<=m do
begin
r[k]:=a[i];
inc(i);
inc(k);
end;
while j<=t do
begin
r[k]:=a[j];
inc(j);
inc(k);
end;
for i:=s to t do a[i]:=r[i];
end;
Begin
readln(n);
for i:=1 to n do read(a[i]);
msort(1,n);
for i:=1 to n do writeln(a[i]);
End.

以下示例代码实现了归并操作。array[]是元素序列,其中从索引p开始到q位置,按照升序排列,同时,从(q+1)到r也已经按照升序排列,merge()函数将把这两个已经排序好的子序列合并成一个排序序列。结果放到array中。

/**
* 0 <= p <= q < r, subarray array[p..q] and array[q+1..r] are already sorted.
* the merge() function merges the two sub-arrays into one sorted array.
*/
void Merge(int array[], int p, int q, int r)
{
int i,k;
int begin1,end1,begin2,end2;
int* temp = (int*)malloc((r-p+1)*sizeof(int));
begin1= p;
end1 = q;
begin2 = q+1;
end2 = r;
k = 0;
while((begin1 <= end1)&&( begin2 <= end2))
{
if(array[begin1] < array[begin2])
{ temp[k] = array[begin1];
begin1++;
}
else
{
temp[k] = array[begin2];
begin2++;
}
k++;
}
while(begin1<=end1 || begin2<=end2)
{
if(begin1<=end1)
{
temp[k++] = array[begin1++];
}
if(begin2<=end2)
{
temp[k++] = array[begin2++];
}
}
for (i = 0; i < =(r - p); i++)
array[p+i] = temp[i];
free(temp);
}

非递归算法(C++)

#include <iostream>
#include <ctime>
#include <cstring>
using namespace std;
/** 将a开头的长为length的数组和b开头长为right的数组 合并 n为数组长度,用于最后一组 */
void Merge(int* data, int a, int b, int length, int n){
int right;
if(b+length-1 >= n-1) right = n-b;
else right = length;
int* temp = new int[length+right];
int i = 0, j = 0;
while(i<=length-1&&j<=right-1){
if(data[a+i] <= data[b+j]){
temp[i+j] = data[a+i]; i++; }
else{ temp[i+j] = data[b+j]; j++; }
}
if(j == right){// a中还有元素,且全都比b中的大,a[i]还未使用
memcpy(data+a+i+j, data+a+i,(length-i)*sizeof(int));
}
memcpy(data+a, temp, (i+j)*sizeof(int) );
delete temp;
}
void MergeSort(int* data, int n){
int step = 1;
while(step < n){
for(int i = 0; i <= n-step-1; i += 2*step)
Merge(data, i, i+step, step, n);
// 将i和i+step这两个有序序列进行合并
// 序列长度为step
// 当i以后的长度小于或者等于step时,退出
step *= 2;
}
}
int main(){
int n;
cin >> n;
int *data = new int[n];
if(!data) exit(1);
int k = n;
while(k --){
cin >> data[n-k-1];
}
clock_t s = clock();
MergeSort(data, n);
clock_t e = clock();
k = n;
while(k --){
cout << data[n-k-1] << ' ';
}
cout << endl;
cout << "the algrothem used " << e-s << " miliseconds."
<< endl;
delete data;
return 0; }

Delphi归并排序完整源代码例子:

procedure TForm1.MergePass(var datas: array of Integer; left, mid,
right: Integer);
var
tmpArr:array of Integer;
arrLen:Integer;
i,k:Integer;
begin1,begin2,end1,end2:Integer;
begin
arrLen := right -left +1;
SetLength(tmpArr,arrLen);
begin1 := left;
end1 := mid;
begin2 := mid+1;
end2 := right;
k:=0;
while((begin1<=end1)and(begin2<=end2)) do
begin
if(datas[begin1]<datas[begin2]) then
begin
tmpArr[k]:=datas[begin1];
Inc(begin1);
end
else
begin
tmpArr[k]:=datas[begin2];
Inc(begin2);
end;
inc(k);
end;
while(begin1<=end1) do
begin
tmpArr[k] := datas[begin1];
Inc(begin1);
Inc(k);
end;
while(begin2<=end2) do
begin
tmpArr[k] := datas[begin2];
Inc(begin2);
Inc(k);
end;
for i := 0 to (right-left) do
begin
datas[left+i]:=tmpArr[i];
end;
end;
//排序主函数,left是数组左下标,0开始。right是数组右下标。
procedure TForm1.MergeSort(var datas: array of Integer; left, right: Integer);
var
mid:Integer;
i:Integer;
begin
mid:=0;
if(left<right) then
begin
mid:=(right+left) div 2;
showLog('中间索引:'+inttostr(mid));
MergeSort(datas,left,mid);
MergeSort(datas,mid+1,right);
MergePass(datas,left,mid,right);
showLog('--->'+getArrayString(datas)); //显示数组中间状态
end;
end;
//调用方法:procedure TForm1.btn1Click(Sender: TObject);
var
inArr:array[0..9] of Integer;
begin
CopyMemory(@inArr[0],@CTabls[0],SizeOf(Integer)*10);
showLog('输入数据:'+getArrayString(inArr));
MergeSort(inArr,0,High(inArr));
showLog('输出数据:'+getArrayString(inArr));
end;

//合并子函数

7复杂度

时间复杂度为O(nlogn) 这是该算法中最好、最坏和平均的时间性能。

空间复杂度为 O(n)

比较操作的次数介于(nlogn) / 2和nlogn - n + 1。

赋值操作的次数是(2nlogn)。归并算法的空间复杂度为:0 (n)

归并排序比较占用内存,但却效率高且稳定的算法。

8归并算法

定义 所谓归并排序是指将两个或两个以上有序的数列(或有序表),合并成一个仍然有序的数列(或有序表)。这样的排序方法经常用于多个有序的数据文件归并成一个有序的数据文件。归并排序的算法比较简单。

基本思想方法:

(1)假设已经有两个有序数列,分别存放在两个数组s,r中;并设i,j分别为指向数组的第一个单元的下标;s有n个元素,r有m个元素。

(2)再另设一个数组a,k指向该数组的第一个单元下标。

(3)算法分析(过程):

procedure merge(s,r,a,i,j,k);
begin
i1:=i;
j1:=j;
k1:=k;
while (i1<n) and (j1<m) do
if s[i1]<=r[j1] then
begin
a[k]:=s[i1];
i1:=i1+1;
k:=k+1;
end
else
begin
a[k]:=r[j1];
j1:=j1+1;
k:=k+1;
end;
while i1<=n do
begin
a[k]:=s[i1];
i1:=i1+1;
k:=k+1;
end;
while j1<=m do
begin
a[k]:=r[j1];
j1:=j1+1;
k:=k+1;
end;
end;

完整的C++源代码

#include<iostream.h>
void Merge(int r[],int r1[],int s,int m,int t)
{
int i=s;int j=m+1;int k=s;
while(i<=m&&j<=t)
{
if(r[i]<=r[j])r1[k++]=r[i++];
else r1[k++]=r[j++];
}
if(i<=m)
while(i<=m) r1[k++]=r[i++];
else while(j<=t)
r1[k++]=r[j++];
for(int l=0;l<8;l++)
r1[l]=r[l];
}
void MergeSort(int r[],int r1[],int s,int t)
{
if(s==t);
else
{
int m=(s+t)/2;
MergeSort(r,r1,s,m);
MergeSort(r,r1,m+1,t);
Merge(r,r1,s,m,t);
}
}
void main()
{
int r[8]={10,3,5,1,9,34,54,565},r1[8];
MergeSort(r,r1,0,7);
for(int q=0;q<8;q++)
cout<<" "<<r[q];
return 0;
}

归并排序的实现方法:

1.自底向上算法

#include <stdio.h>
#include <time.h>
void Merge(int *a,int low,int mid,int high)
{
int i = low,j = mid + 1,k = 0;
int *temp = (int *)malloc((high - low + 1)*sizeof(int));
while(i <= mid && j <= high)
a[i] < a[j] ? (temp[k++] = a[i++]):(temp[k++] = a[j++]);
while(i <= mid)
temp[k++] = a[i++];
while(j <= high)
temp[k++] = a[j++];
memcpy(a + low,temp,(high -low + 1)*sizeof(int));
free(temp);
}
void MergeSort(int *a,int n)
{
int length;
for(length = 1;length < n;length *= 2)
{
int i;
for( i = 0;i + 2*length - 1 <= n - 1;i += 2*length)
Merge(a,i,i+length-1,i+2*length -1);
if(i + length<= n - 1)//尚有两个子文件,其中后一个长度小于length
  Merge(a,i,i + length -1,n - 1);
}
}
int main()
{
int n;
cin >> n;
int *data = new int[n];
if(!data) exit(1);
int k = n;
while(k --)
{
cin >> data[n-k-1];
}
clock_t s = clock();
MergeSort(data, n);
clock_t e = clock();
k = n;
while(k --){
cout << data[n-k-1] << ' ';
}
cout << endl;
cout << "the algrothem used " << e-s << " miliseconds."<< endl;
delete data;
return 0;
}

2.自顶向下

void Merge(int r[],int r1[],int s,int m,int t)
{
int i=s;int j=m+1;int k=s;
while(i<=m&&j<=t)
{
if(r[i]<=r[j])r1[k++]=r[i++];
else r1[k++]=r[j++];
}
while(i<=m) r1[k++]=r[i++];
while(j<=t)
r1[k++]=r[j++];
for(int l=0;l<8;l++)
r[l]=r1[l];
}
void MergeSort(int r[],int r1[],int s,int t)
{
if(s==t);
else{
int m=(s+t)/2;
MergeSort(r,r1,s,m);
MergeSort(r,r1,m+1,t);
Merge(r,r1,s,m,t);
}
}
posted @ 2019-07-16 13:05  prestige  阅读(209)  评论(0编辑  收藏  举报