归并排序

归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并
 
 
中文名
归并排序
外文名
Merge Sort
稳定性
稳定
时间复杂度
O(n log n)
空间复杂度
T(n)
发明者
约翰·冯·诺伊曼

归并操作

编辑 语音
归并操作,也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{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;
逆序数为14;

算法描述

编辑 语音
归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
归并排序是稳定的排序.即相等的元素的顺序不会改变.如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时输出的 1(1) 2(3) 2(4) 3(2) 5(5) 中的2 和 2 是按输入的顺序.这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要。归并排序的比较次数小于快速排序的比较次数,移动次数一般多于快速排序的移动次数。

排序

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

求逆序对数

具体思路是,在归并的过程中计算每个小区间的逆序对数,进而计算出大区间的逆序对数(也可以用树状数组来求解)
scala代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
objectMainextendsApp{
    varreverse_pairs = 0//逆序数
    defmsort[T](cmp:(T, T) => Boolean)(l:List[T]):List[T] = {
        defmerge(l1:List[T], l2:List[T]):List[T]=(l1, l2)match{
            case(Nil, _) => l2
            case(_, Nil) => l1
            case(x::left1, y::left2) =>
                if(cmp(x, y))
                    x::merge(left1, l2)
                else{
                    reverse_pairs += l1.length
                    y::merge(l1, left2)
                }
        }
        valn = l.length / 2
        if(n == 0)
            return l
        else{
            val(l1, l2) = l.splitAt(n)
            merge(msort(cmp)(l1), msort(cmp)(l2))
        }
    }
    println(msort((x:Int, y:Int) => x<y)(List(5, 4, 3, 2, 7,6 )))
    println(reverse_pairs)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//采用自上而下的递归方法
public class MergeSort {
    private int[] Sort(int[]arr){
        //若待排序数组长度<2,则直接返回
        if (arr.length<2)
            return arr;
        int mid=arr.length/2;
        return process(Sort(Arrays.copyOfRange(arr,0,mid)),Sort(Arrays.copyOfRange(arr,mid,arr.length)));
    }
    //合并两个有序数组
    private int[] process(int []arr1,int[]arr2){
        int[] re=new int[arr1.length+arr2.length];
        //i为arr1的index,j为arr2的index,k为re的index
        int i=0,j=0,k=0;
        //获取两个数组较小元素,填入re内
        while (i<arr1.length&&j<arr2.length)
            re[k++]=(arr1[i]<arr2[j])?arr1[i++]:arr2[j++];
        //若arr1有元素剩余,全部填入re内
        if(i!=arr1.length)
            for(int p=i;p<arr1.length;p++)
                re[k++]=arr1[p];
        //若arr2内有元素剩余,全部填入re内
        if(j!=arr2.length)
            for(int p=j;p<arr2.length;p++)
                re[k++]=arr2[p];
 
        return re;
    }

示例代码

编辑 语音
归并排序原理
归并排序具体工作原理如下(假设序列共有n个元素):
将序列每相邻两个数字进行归并操作(merge),形成floor(n/2+n%2)个序列,排序后每个序列包含两个元素
将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
重复步骤2,直到所有元素排序完毕
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//归并排序
func mergeSort(_ arr: inout [Int]) {
    var gap = 1;
    while gap < arr.count {
        mergePass(&arr, gap: gap);
         
        gap *= 2;
    }
}
 
//分解合并序列,gap表示子序列的元素个数
func mergePass(_ arr: inout [Int], gap: Int) {
    var i = 0;
    let count = arr.count;
     
    while i + 2 * gap - 1 < count {
        mergeArray(&arr, low: i, mid: i + gap - 1, high: i + 2 * gap - 1);
         
        i += 2 * gap;
    }
     
    //合并剩余的序列
    if i + gap - 1 < count {
        mergeArray(&arr, low: i, mid: i + gap - 1, high: count - 1);
    }
}
 
//合并两个序列
func mergeArray(_ arr: inout [Int], low: Int, mid: Int, high: Int) {
     
    var i = low;
    var j = mid + 1;
    var k = 0;
     
    var array = Array<Int>(repeating: 0, count: high - low + 1);
     
    while i <= mid && j <= high {
        if arr[i] < arr[j] {
            array[k] = arr[i];
            i += 1;
            k += 1;
        else {
            array[k] = arr[j];
            j += 1;
            k += 1;
        }
    }
     
    while i <= mid {
        array[k] = arr[i];
        i += 1;
        k += 1;
    }
     
    while j <= high {
        array[k] = arr[j];
        j += 1;
        k += 1;
    }
     
    //将排序好的序列复制回原数组
    k = 0;
    for i in low...high {
        arr[i] = array[k];
         
        k += 1;
    }
}
 
 
var array = [2, 5, 8, 9, 10, 4, 3, 16, 1, 7, 8];
mergeSort(&array);
print(array); 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func mergeSort(r []int) []int {
    length := len(r)   
    if length <= 1 {
        return 
    }   
    num := length / 2
    left := mergeSort(r[:num])   
    right := mergeSort(r[num:])   
    return merge(left, right)
}
func merge(left, right []int) (result []int) {   
    l, r := 0, 0   
    for l < len(left) && r < len(right) {
        if left[l] < right[r] {         
            result = append(result, left[l])         
            l++      
        else {         
            result = append(result, right[r])         
            r++      
        }   
    }   
    result = append(result, left[l:]...)   
    result = append(result, right[r:]...)   
    return
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package MergeSort;
public class MergeSort {   
    public static int[] mergeSort(int[] nums, int l, int h) {
        if (l == h)
            return new int[] { nums[l] };
         
        int mid = l + (h - l) / 2;
        int[] leftArr = mergeSort(nums, l, mid); //左有序数组
        int[] rightArr = mergeSort(nums, mid + 1, h); //右有序数组
        int[] newNum = new int[leftArr.length + rightArr.length]; //新有序数组
         
        int m = 0, i = 0, j = 0
        while (i < leftArr.length && j < rightArr.length) {
            newNum[m++] = leftArr[i] < rightArr[j] ? leftArr[i++] : rightArr[j++];
        }
        while (i < leftArr.length)
            newNum[m++] = leftArr[i++];
        while (j < rightArr.length)
            newNum[m++] = rightArr[j++];
        return newNum;
    }
    public static void main(String[] args) {
        int[] nums = new int[] { 9876543210 };
        int[] newNums = mergeSort(nums, 0, nums.length - 1);
        for (int x : newNums) {
            System.out.println(x);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void Sort(int[] a, int f, int e)
{
    if (f < e)
    {
        int mid = (f + e) / 2;
        Sort(a, f, mid);
        Sort(a, mid + 1, e);
        MergeMethid(a, f, mid, e);
    }
}
private static void MergeMethid(int[] a, int f, int mid, int e)
{
    int[] t = new int[e - f + 1];
    int m = f, n = mid + 1, k = 0;
    while(n <= e && m <= mid)
    {
        if (a[m] > a[n]) t[k++] = a[n++];
        else t[k++] = a[m++];
    }
    while (n < e + 1) t[k++] = a[n++];
    while (m < mid + 1) t[k++] = a[m++];
    for (k = 0, m = f; m < e + 1; k++, m++) a[m] = t[k];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def MergeSort(lists):
    if len(lists) <= 1:
        return lists
    num = intlen(lists) / 2 )
    left = MergeSort(lists[:num])
    right = MergeSort(lists[num:])
    return Merge(left, right)
def Merge(left,right):
    r, l=00
    result=[]
    while l<len(left) and r<len(right):
        if left[l] <= right[r]:
            result.append(left[l])
            += 1
        else:
            result.append(right[r])
            += 1
    result += list(left[l:])
    result += list(right[r:])
    return result
print MergeSort([123456790212345])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdlib.h>
#include <stdio.h>
 
void Merge(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex)
{
    int i = startIndex, j=midIndex+1, k = startIndex;
    while(i!=midIndex+1 && j!=endIndex+1)
    {
        if(sourceArr[i] > sourceArr[j])
            tempArr[k++] = sourceArr[j++];
        else
            tempArr[k++] = sourceArr[i++];
    }
    while(i != midIndex+1)
        tempArr[k++] = sourceArr[i++];
    while(j != endIndex+1)
        tempArr[k++] = sourceArr[j++];
    for(i=startIndex; i<=endIndex; i++)
        sourceArr[i] = tempArr[i];
}
 
//内部使用递归
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex)
{
    int midIndex;
    if(startIndex < endIndex)
    {
        midIndex = startIndex + (endIndex-startIndex) / 2;//避免溢出int
        MergeSort(sourceArr, tempArr, startIndex, midIndex);
        MergeSort(sourceArr, tempArr, midIndex+1, endIndex);
        Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);
    }
}
 
int main(int argc, char * argv[])
{
    int a[8] = {50, 10, 20, 30, 70, 40, 80, 60};
    int i, b[8];
    MergeSort(a, b, 0, 7);
    for(i=0; i<8; i++)
        printf("%d ", a[i]);
    printf("\n");
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function msort(array &$arr, $low, $high){
    if($low>=$high)
        return;
    $mid = floor(($low+$high)/2);
 
    msort($arr, $low, $mid);
    msort($arr, $mid+1, $high);
 
    mmerge($arr, $low, $mid, $high);
}
 
function mmerge(array &$arr, $low, $mid, $high){
 
    //复制数据备用
    $aa = [];
    for($k=$low; $k<=$high; $k++){
        $aa[$k] = $arr[$k];
    }
 
    //合并数组
    $i = $low;//左边起点
    $j = $mid+1;//右边起点
    for($k=$low; $k<=$high; $k++){
        if($i>$mid){//左边处理完成,使用右边
            $arr[$k] = $aa[$j++];
        }elseif($j>$high){//右边处理完成,使用左边
            $arr[$k] = $aa[$i++];
        }elseif($aa[$i]>$aa[$j]){//左边比右边大,用右边
            $arr[$k] = $aa[$j++];
        }else{
            $arr[$k] = $aa[$i++];
        }
    }
}
 
$a = [4,3,6,7,5,1,9,0,2];
msort($a, 0, count($a)-1);
print_r($a);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
program mergesort_1;
const maxn=7;
type
    arr=array[1..maxn] of integer;
var
    a,b,c:arr;
    i:integer;
procedure merge(r:arr; l,m,n:integervar 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 do begin
            k:=k+1;
            r2[k]:=r[p];
        end;
        if j<=n then for p:=j to 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)div2;
            mergesort(r,c,s,k);
            mergesort(r,c,k+1,t);
            merge(c,s,k,t,r1)
        end;
    end;
begin
    write('Enterdata:');
    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.
//============================================
program mergesort_2;
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 do a[i]:=r[i];
    end;
begin
    readln(n);
    for i:=1 to do read(a[i]);
    msort(1,n);
    for i:=1 to do writeln(a[i]);
end.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Sub MergeSort(Array() As Integer, First As Integer, Last As Integer)
Dim mid As Integer = 0
If first<last Then
 mid = (first+last)\ 2
MergeSort(Array, first, mid);
MergeSort(Array, mid+1, last);
Merge(Array, first, mid, last);
End If
End Sub
/*
以下示例代码实现了归并操作。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);
}
使用递归的代码如下。优点是描述算法过程思路清晰,缺点是使用递归,mergeSort()函数频繁地自我调用。长度为n的数组最终会调用mergeSort()函数 2n-1次,这意味着一个长度超过1500的数组会在Firefox上发生栈溢出错误。可以考虑使用迭代来实现同样的功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function merge(left, right){
    var result=[];
    while(left.length>0 && right.length>0){
        if(left[0]<right[0]){
        /*shift()方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。*/
            result.push(left.shift());
        }else{
            result.push(right.shift());
        }
    }
    return result.concat(left).concat(right);
}
function mergeSort(items){
    if(items.length == 1){
        return items;
}
var middle = Math.floor(items.length/2),
    left = items.slice(0, middle),
    right = items.slice(middle);
    return merge(mergeSort(left), mergeSort(right));
}
非递归算法 (javaScript)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function mergePass(arr = [], temp = new Array(arr.length), N = arr.length, length = 1){ // 将每个元素看作是相邻的数组长度为1。
  let t; // 迭代深度。
  for (t = 0; Math.pow(2,t) < N; t++, length *= 2) { // 每次跳过的长度翻倍。
    const even = t%2 === 0; // 复用 arr 和 temp 来回赋值。
    for (let left = 0;  left < N; left += 2 * length) { // 左边数组起始位置 left 从0开始。
      const middle = left + length < N ? left + length : left; // 右边数组起始位置 middle 就是left + 一个数组长度length 但是不要超过 N 。
      const right = left + (2 * length) < N ? left + (2 * length) : N; // 右边界 right 就是 left + 两个数组长度。
      merge(even ? arr : temp, even ? temp : arr, left, middle, right); // 合并每两个相邻的数组。
    }
  }
  if(t % 2 === 0){
        return arr;//返回arr
    }
  return temp; // 返回 temp 。
}
function merge(arr, temp, left, middle, right){
  const leftEnd = middle - 1; // 通过右边数组的起始位置得到左边数组的结束位置。
  while (left <= leftEnd && middle < right) { // 如果‘指针’没有越界。
    if (arr[left] > arr[middle]) { // 如果左边数组第一个元素比右边数组第一个元素大。
      temp[left + middle - leftEnd -1] = arr[middle++]; // 将右边数组最小的放入有序数组 temp(初始值为空)。
    else {
      temp[left + middle - leftEnd -1] = arr[left++]; // 将左边数组最小的放入有序数组 temp(初始值为空)。
    }
  }
  while(left > leftEnd && middle < right){ // 如果左边数组放完了,右边数组还有元素。
    temp[left + middle - leftEnd -1] = arr[middle++]; // 那么依次将右边数组剩余的元素放入 temp 。
  }
  while(left <= leftEnd && middle >= right){ // 如果右边数组放完了,左边数组还有元素
    temp[left + middle - leftEnd -1] = arr[left++]; // 那么依次将左边数组剩余的元素放入 temp 。
  }
}
非递归算法(C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include<iostream>
#include<ctime>
#include<cstring>
#include<cstdlib>
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(temp + i + j, data + a + i, (length - i) * sizeof(int));
 }
  else if(i == length){
      memcpy(temp + i + j, data + b + j, (right - j)*sizeof(int));
  }
 memcpy(data+a, temp, (right + length) * 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 algorithm used"<<e-s<<"miliseconds."<<endl;
 delete data;
 return 0;
}
 
递归算法:
#include<iostream>
using namespace std;
void merge(int *data, int start, int mid, int end, int *result)
{
    int i, j, k;
    i = start;
    j = mid + 1;                        //避免重复比较data[mid]
    k = 0;
    while (i <= mid && j <= end)        //数组data[start,mid]与数组(mid,end]均没有全部归入数组result中去
    {
        if (data[i] <= data[j])         //如果data[i]小于等于data[j]
            result[k++] = data[i++];    //则将data[i]的值赋给result[k],之后i,k各加一,表示后移一位
        else
            result[k++] = data[j++];    //否则,将data[j]的值赋给result[k],j,k各加一
    }
    while (i <= mid)                    //表示数组data(mid,end]已经全部归入result数组中去了,而数组data[start,mid]还有剩余
        result[k++] = data[i++];        //将数组data[start,mid]剩下的值,逐一归入数组result
    while (j <= end)                    //表示数组data[start,mid]已经全部归入到result数组中去了,而数组(mid,high]还有剩余
        result[k++] = data[j++];        //将数组a[mid,high]剩下的值,逐一归入数组result
 
    for (i = 0; i < k; i++)             //将归并后的数组的值逐一赋给数组data[start,end]
        data[start + i] = result[i];    //注意,应从data[start+i]开始赋值
}
void merge_sort(int *data, int start, int end, int *result)
{
    if (start < end)
    {
        int mid = start + (end-start) / 2;//避免溢出int
        merge_sort(data, start, mid, result);                    //对左边进行排序
        merge_sort(data, mid + 1, end, result);                  //对右边进行排序
        merge(data, start, mid, end, result);                    //把排序好的数据合并
    }
}
void amalgamation(int *data1, int *data2, int *result)
{
    for (int i = 0; i < 10; i++)
        result[i] = data1[i];
    for (int i = 0; i < 10; i++)
        result[i + 10] = data2[i];
}
int main()
{
    int data1[10] = { 1,7,6,4,9,14,19,100,55,10 };
    int data2[10] = { 2,6,8,99,45,63,102,556,10,41 };
    int *result = new int[20];                              
    int *result1 = new int[20];
    amalgamation(data1, data2, result);
    for (int i = 0; i < 20; ++i)
        cout << result[i] << "  ";
    cout << endl;
    merge_sort(result, 0, 19, result1);
    for (int i = 0; i < 20; ++i)
        cout << result[i] << "  ";
    delete[]result;
    delete[]result1;
    return 0;
}
二路归并
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
Const
FI='in.txt';
FO='out.txt';
MaxN=10000;
Type
TIndex=Longint;
TDat=Array[0..MaxN]OfTIndex;
Var
N:TIndex;
Dat:TDat;
Tmp:TDat;
ProcedureMerge(L,Mid,R:TIndex);
Var
P1,P2:TIndex;
E1,E2:TIndex;
P:TIndex;
I:TIndex;
Begin
P1:=L;
P2:=Mid+1;
P:=L;
Repeat
If(Dat[P1]<=Dat[P2])Then
Begin
Tmp[P]:=Dat[P1];
Inc(P1);
Inc(P);
End
Else
Begin
Tmp[P]:=Dat[P2];
Inc(P2);
Inc(P);
End;
Until(P1=Mid+1)Or(P2=R+1);
If(P1=Mid+1)Then
Begin
E1:=P2;
E2:=R;
End
Else
Begin
E1:=P1;
E2:=Mid;
End;
ForI:=E1ToE2Do
Begin
Tmp[P]:=Dat[I];
Inc(P);
End;
End;
ProcedureSort(L,R:TIndex);
Var
Mid:TIndex=0;
Begin
Mid:=(L+R)Shr1;
If(L<Mid)Then
Sort(L,Mid);
If(Mid+1<R)Then
Sort(Mid+1,R);
Merge(L,Mid,R);
ForMid:=LToRDo
Dat[Mid]:=Tmp[Mid];
End;
ProcedureInit;
Var
I:TIndex;
Begin
FillChar(Dat,SizeOf(Dat),0);
Readln(N);
ForI:=1ToNDo
Read(Dat[I]);
End;
ProcedureMain;
Begin
Sort(1,N);
End;
ProcedureFinal;
Var
I:TIndex;
Begin
ForI:=1ToNDo
Write(Dat[I],'');
Writeln;
End;
Begin
Assign(Input,FI);
Assign(Output,FO);
Reset(Input);
Rewrite(Output);
Init;
Main;
Final;
Close(Input);
Close(Output);
End.
Delphi
归并排序完整源代码例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//合并子函数
procedureTForm1.MergePass(vardatas:arrayofInteger;left,mid,
right:Integer);
var
tmpArr:arrayofInteger;
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;
fori:=0to(right-left)do
begin
datas[left+i]:=tmpArr[i];
end;
end;
//排序主函数,left是数组左下标,0开始。right是数组右下标。
procedureTForm1.MergeSort(vardatas:arrayofInteger;left,right:Integer);
var
mid:Integer;
i:Integer;
begin
mid:=0;
if(left<right)then
begin
mid:=(right+left)div2;
showLog('中间索引:'+inttostr(mid));
MergeSort(datas,left,mid);
MergeSort(datas,mid+1,right);
MergePass(datas,left,mid,right);
showLog('--->'+getArrayString(datas));//显示数组中间状态
end;
end;
//调用方法:procedureTForm1.btn1Click(Sender:TObject);
var
inArr:array[0..9]ofInteger;
begin
CopyMemory(@inArr[0],@CTabls[0],SizeOf(Integer)*10);
showLog('输入数据:'+getArrayString(inArr));
MergeSort(inArr,0,High(inArr));
showLog('输出数据:'+getArrayString(inArr));
end;

复杂度

编辑 语音
归并排序比较占用内存,但却是一种效率高且稳定的算法。
改进归并排序在归并时先判断前段序列的最大值与后段序列最小值的关系再确定是否进行复制比较。如果前段序列的最大值小于等于后段序列最小值,则说明序列可以直接形成一段有序序列不需要再归并,反之则需要。所以在序列本身有序的情况下时间复杂度可以降至O(n)
TimSort可以说是归并排序的终极优化版本,主要思想就是检测序列中的天然有序子段(若检测到严格降序子段则翻转序列为升序子段)。在最好情况下无论升序还是降序都可以使时间复杂度降至为O(n),具有很强的自适应性。
 
最好时间复杂度
最坏时间复杂度
平均时间复杂度
空间复杂度
稳定性
传统归并排序
O(nlogn)
O(nlogn)
O(nlogn)
T(n)
稳定
改进归并排序 [1] 
O(n)
O(nlogn)
O(nlogn)
T(n)
稳定
TimSort
O(n)
O(nlogn)
O(nlogn)
T(n)
稳定
注:文献 [1]  是一种改进的原地归并算法,空间复杂度为O(1)。在表格里的改进归并排序只是引入其预先判断的这一步,这样便可使传统归并排序时间复杂度降至O(n)。

归并算法

编辑 语音
定义
所谓归并排序是指将两个或两个以上有序的数列(或有序表),合并成一个仍然有序的数列(或有序表)。这样的排序方法经常用于多个有序的数据文件归并成一个有序的数据文件。归并排序的算法比较简单。
基本思想方法:
(1)假设已经有两个有序数列,分别存放在两个数组s,r中;并设i,j分别为指向数组的第一个单元的下标;s有n个元素,r有m个元素。
(2)再另设一个数组a,k指向该数组的第一个单元下标。
(3)算法分析(过程):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
proceduremerge(s,r,a,i,j,k);
begin
i1:=i;
j1:=j;
k1:=k;
while(i1<n)and(j1<m)do
ifs[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;
whilei1<=ndo
begin
a[k]:=s[i1];
i1:=i1+1;
k:=k+1;
end;
whilej1<=mdo
begin
a[k]:=r[j1];
j1:=j1+1;
k:=k+1;
end;
end;
完整的C++源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
 
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 n = s; n <= t; n++)
        r[n] = r1[n];
}
 
void MergeSort(int r[], int r1[], int s, int t)
{
    if (s < t)
    {
        int m = (s + t) / 2;
        MergeSort(r, r1, s, m);
        MergeSort(r, r1, m + 1, t);
        Merge(r, r1, s, m, t);
    }
}
 
int 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++)
        std::cout << r[q] << std::ends;
    return 0;
}
归并排序的实现方法:
1.自底向上算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<stdio.h>
#include<time.h>
voidMerge(int*a,intlow,intmid,inthigh){
inti=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);
}
voidMergeSort(int*a,intn){
intlength;
for(length=1;length<n;length*=2){
inti;
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);
}
}
intmain(){
intn;
cin>>n;
int*data=new
int[n];
if(!data)
exit(1);
intk=n;
while(k--){
cin>>data[n-k-1];
}
clock_ts=clock();
MergeSort(data,n);
clock_te=clock();
k=n;
while(k--){
cout<<data[n-k-1]<<'';
}
cout<<endl;
cout<<"thealgrothemused"<<e-s<<"miliseconds."<<endl;
deletedata;
return0;
}
2.自顶向下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);
    }
}

应用实例

编辑 语音
NOIP2013 提高组火柴排队
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include<bits/stdc++.h>
using namespace std;
int c[1000005],t[1000005],mod=99999997;
long long cnt=0;
struct edge{
    int num,i;
}a[1000005],b[1000005];
void merge(int l,int r)
{
    if(l==r)
        return;
    int mid=(l+r)/2;
    merge(l,mid);
    merge(mid+1,r);
    int x=l,y=mid+1,tot=l;
    while(x<=mid&&y<=r)
        if(c[x]<=c[y])
            t[tot++]=c[x++];
        else
        {
             cnt+=(mid-x+1)%mod;
             cnt%=mod;
             t[tot++]=c[y++];
        }
    while(x<=mid)
        t[tot++]=c[x++];
    while(y<=r)
        t[tot++]=c[y++];
    for(int i=l;i<= r;++i)
        c[i]=t[i];
}
bool cmp(edge a,edge b)
{
    return a.num<b.num;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;++i)
    {
        cin>>a[i].num;
        a[i].i=i;
    }
    for(int i=1;i<=n;++i)
    {
        cin>>b[i].num;
        b[i].i=i;
    }
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1,cmp);
    for(int i=1;i<=n;++i)
        c[a[i].i]=b[i].i;
    merge(1,n);
    cout<<cnt<<endl;
    return 0;
}
 
 归并
posted @ 2021-11-09 13:47  笨笨韩  阅读(99)  评论(0)    收藏  举报