目录

  • 归并排序
  • 概述
  • 实现原理
  • 代码实现


归并排序

概述

归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

简而言之就是,首先将数组分成两半,再分成两半…直到每部分只剩一个元素,则开始合并,左右两个元素(数组),比较后,形成一个有序的数组,再和其他也合并完的有序数组再次合并,如此反复直到排序完成。

具体如下图所示

java 编码相同数据合并 java实现合并排序_排序算法


图源:百度百科

实现原理

以上图举例,其实不是一次将其分成单独元素的,而是先从左侧开始,不断递归,一次分到底。

先分成10和4,作为左右数组,将其排序生成新的数组{10,4},此数组为上一层的左数组,此时结束一层递归进入上一层的下一语句:求出右数组,而右数组继续进入递归,将其分成6 3作为左右数组重新排序为{3,6},此数组为上一层递归的右数组,与{10,4}重新排序为新的数组作为上一层的左数组,完成后进入上一层的下一语句,即为分割{8,2,5,7}数组然后重复类似上述的操作即可。

值得一提的是,上述操作合并时,需要定义三个指针,开始时分别指向左数组的第一个、右数组的第一个、新数组的第一个元素,而当左数组的值进入新数组,则左数组指针后移、新数组指针后移,反之亦然。

代码实现

package sort;

public class MergeSort {

    public static void main(String[] args) {
//        int[] array = {1,5,4,8,7,6,2,3,9};
//        int i = 0;
//        int j = array.length-1;
//        int[] ints = mergeSort(array,i, j);
//        for (int anInt : ints) {
//            System.out.print(anInt);
//        }
        int[] array = new int[80000];
        for (int i = 0; i < array.length; i++) {
            array[i] = (int)(Math.random()*800000);
        }
        int i = 0;
        int j = array.length-1;
        long startTime=System.currentTimeMillis();
        mergeSort(array, i, j);
        long endTime=System.currentTimeMillis();
        System.out.println("程序运行时间: "+(endTime - startTime)+"ms");
        //归并排序排序80k长度的数组仅需0.05s左右,速度和快速排序相当,
        // 而且它是稳定的排序.即相等的元素的顺序不会改变.
        // 就是说,假如有两个1在数组中,排序完毕后,之前在前的1还是在前面
    }
    public static int[] mergeSort(int[] array, int i, int j){
        if (i == j){
            //如果 i == j,说明数组已经分割到只有一个元素了,直接返回给上一层即可
            return new int[]{ array[i] };
        }
        //每次分割(i + j - 1) / 2,这里减1是为了下面递归的时候,i 和 j能够有相等的机会
        int mid = (i + j - 1) / 2 ;
        //分割出的左侧的有序数组
        int[] leftArray = mergeSort(array, i, mid);
        //分割出的右侧的有序数组
        int[] rightArray = mergeSort(array, mid + 1, j);
        //左侧和右侧合并的新数组
        int[] newArray = new int[leftArray.length+rightArray.length];

        int l = 0;//操作左侧数组的下标
        int r = 0;//操作右侧数组的下标
        int n = 0;//操作新数组的下标
        // l,r 右一个 >= 它对应数组的长度的时候,说明该数组的数字已经全部分配完了,
        // 只需要另一个数组依次加入到新数组中即可
        while (l<leftArray.length && r<rightArray.length){
            //如果左侧数组的元素大于右侧,则进入新数组,且n,l后移
            //else 反之亦然
            if (leftArray[l] <= rightArray[r]){
                newArray[n] = leftArray[l];
                n++;
                l++;
            } else {
                newArray[n] = rightArray[r];
                n++;
                r++;
            }
        }
        //能到这里说明有一个数组是分配完了的,下面两个循环只需要判断哪个没有分配完,则直接加入到新数组中即可
        while (l<leftArray.length){
            newArray[n] = leftArray[l];
            n++;
            l++;
        }
        while (r<rightArray.length){
            newArray[n] = rightArray[r];
            n++;
            r++;
        }
        return newArray;
    }

}