归并排序

来源:互联网 发布:疯狂原始人 知乎 编辑:程序博客网 时间:2024/06/05 22:56

归并排序

基本思想

归并排序其实是分治思想的体现,先解决小规模的问题,小规模的问题解决后,大的问题自然就会得到解决。

例如对于长度为15的数组a,先将子数组a[0-7]排序,再将a[8-14]排序,这两个子数组排序后归并结果即可。整个过程其实可以用一个二叉树来表示:

这里写图片描述

图1 归并排序示意图

归并两个已经排序的子数组的过程中,需要将这两个子数组的值复制到另一个临时数组中,再从小到大归并到原数组。

代码实现

代码实现如下:

/** * <p>文件描述: 归并排序</p> * * @Author luanmousheng * @Date 17/8/1 下午2:46*/public class MergeSort {    //归并时用到的临时空间    private static int[] temp;    public static void sort(int[] arr) {        if (arr == null) {            return;        }        sort(arr, 0, arr.length - 1);    }    private static void sort(int[] arr, int lo, int hi) {        //如果数组开始位置大于等于结束位置,说明本次排序完成,返回       if (lo >= hi) {           return;       }       //取中间位置       int mid = (lo + hi) /2;       //将左边的部分排序       sort(arr, lo, mid);       //将右边的部分排序       sort(arr, mid+1, hi);       //此时两边已经是排序的,归并结果       merge(arr, lo, mid, hi);    }    private static void merge(int[] arr, int lo, int mid, int hi) {        //先将原数组lo到hi处的值复制到临时数组        for (int i = lo; i <= hi; i++) {            temp[i] = arr[i];        }        //i是左边数组的开始位置        int i = lo;        //j是右边数组的开始位置        int j = mid+1;        //遍历原数组lo到hi位置,        for (int k = lo; k <= hi; k++) {            if (i > mid) {                //左边部分已经遍历完                arr[k] = temp[j++];            } else if (j > hi) {                //右边部分已经遍历完                arr[k] = temp[i++];            } else if (temp[i] < temp[j]) {                //取较小的值放到原来数组的位置k处                arr[k] = temp[i++];            } else {                arr[k] = temp[j++];            }        }    }    public static void main(String[] args) {        int[] arr = {4,3,5,8,11,9,2,7};        temp = new int[arr.length];        sort(arr);        for (int i = 0; i < arr.length; i++) {            System.out.print(arr[i] + ", ");        }    }}

这里的逻辑就是先递归排序左边的数组,然后递归排序右边的数组,接着归并这两个已经排序完成的数组。

这里写图片描述

图2 两个有序数组的归并

图2 归并时会将原数组的数据复制到临时数组,临时数组上放上两个指针i和j,分别指向左边数组和右边数组的开始位置,比较这两个位置的值并将较小的值存储到原数组,直到这两个子数组遍历完成。

复杂度

归并排序的空间复杂度为O(n),时间复杂度为O(nlogn)。

原创粉丝点击