算法时空第6讲 归并排序

归并排序

Posted by Twany on July 10, 2019

归并排序采用了分而治之的策略和迭代的方法,何为分而治之?

  • 分:将原问题分成若干小问题
  • 治:分别处理小问题
  • 合:以小问题的解构建原问题的解

难在两处:

  • 如何巧妙地“分”?
  • 如何巧妙地“合”?

下图为归并排序思路:

先分再合

代码实现:

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
public class Merge
{
    //归并排序
    public static void main(String[] args) {
        //需要排序的数组
        int[] arr = {9,5,3,7,4,9,3,5,1};

        //第一次归并(第一个参数为排序数组;第二个为排序左索引,第二个为右索引)
        merge_sort(arr, 0, arr.length-1);

        //输出排序好的数组
        for (int i : arr) {
            System.out.println("排序之后" + i);
        }
    }

    //拆分过程
    private static void merge_sort(int[] arr, int left, int right) {
        //求出要拆分的数组arr中点索引
        //int mid = (left+right)/2;
        int mid = left + (right-left)/2; //化简其实等于上处的式子,但这样做避免栈溢出

        //如果数组中只有一个元素,返回
        if(right == mid){
            return;
        }else{
            //左边数组排序
            merge_sort(arr, left, mid);

            //右边数组排序
            merge_sort(arr, mid+1, right);
            
            //两边数组合并
            merge(arr, left, mid, right);
        }
    }
  
    private static void merge(int[] arr, int left, int mid, int right){
        /*
        * 要合并的是两个数组,给两个数组指针索引,改变则索引加一
        * @i 左边数组索引
        * @j 右边数组索引
        */
        int i=left;
        int j=mid+1;

        //复刻一个新的数组,新数组只复刻要排序的数组部分(注意长度)
        int[] auk = new int[right-left+1];

       //数组赋值,先把较小的数移到新数组
        while(i <= mid && j <= right){
            if(arr[i] < arr[j]){
                auk[m++] = arr[i++];
            }else{
                auk[m++] = arr[j++];
            }
        }

        //如果只有一个数组排序完,另一个还未完
        //把左边数组中剩余的数移入数组
        while(i <= mid){
            auk[m++] = arr[i++];
        }

        //右边同理
        while(j <= mid){
            auk[m++] = arr[j++];
        }

        //新数组覆盖原数组
        for (int k = 0; k < right-left+1; k++) {
            arr[left + k] = auk[k];
        }
    }
}

难点:

  • 首先要理解merge_sort(),此方法作用是递归调用:使数组变为长度为1。之后再调用合并
  • merge()方法的左右边界取值,注意是否加一减一,因为数组是从0开始,以及mid的取值

cookie

  • 防止栈溢出:int mid = left + (right-left)/2; 。让我写肯定是 mid = (left+right)/2,但如果最大长度为right位,就尴尬了
  • 代码简洁之道:
    1
    2
    3
    4
    5
    6
    7
    
    while(i <= mid && j <= right){
    if(arr[i] < arr[j]){
        auk[m++] = arr[i++];
    }else{
        auk[m++] = arr[j++];
    }
    }
    

    很简洁的代码,非常值得学习!

参考链接:Java实现归并排序