归并排序原理及Java实现

来源:互联网 发布:祖传秘方淘宝可以卖吗 编辑:程序博客网 时间:2024/06/06 09:18

    归并排序是基于归并操作的一种效率较高的排序算法,同快速排序一样,归并排序也是分治法的一种应用。其时间复杂度为:O(nlogn),最好和最坏情况下都是O(nlogn)。空间复杂度为O(n)。归并排序是一种稳定的排序算法(两个相等的数据,在排序后其先后顺序不变,因为归并不涉及两个相等的数据进行交换)。

  归并排序的思想

     归并排序的思想就是将待排序数列num[n]看做是n个有序数列,然后将相邻的有序表进行归并。归并排序其实就是做两件事:

    (1)分解:将待排序数列进行折半划分;

    (2)归并:将划分后的序列段和并排序。

     例如:有一待排序序列nums[14, 12, 15, 13, 11, 16],下图是其分解和归并过程:


     我们首先来分析第二步归并过程。

   合并两个有序数列

     要想合并两个有序数列很简单,只需要比较两个数列的第一个数,然后取最小的,然后移动被取的那个数列的指针。直到有一个数列取完指针为空,然后再把另一个不为空的数列复制过去。

     核心代码为:

 //合并有序数组序列,利用辅助数组。    public static void merge(int[] nums, int left, int mid, int right, int[] result) {        int left1 = left;        int right1 = mid;        int left2 = mid + 1;        int right2 = right;        int k = 0;        //先把较小的数移入到临时数组        while (left1 <= right1 && left2 <= right2) {            if (nums[left1] <= nums[left2]) {                result[k++] = nums[left1++];            } else {                result[k++] = nums[left2++];            }        }        //把左边剩余的数移入        while (left1 <= right1) {            result[k++] = nums[left1++];        }        //把右边剩余的数移入        while (left2 <= right2) {            result[k++] = nums[left2++];        }        //将排序好的覆盖到nums数组        for (int i = 0; i < k; i++) {                nums[left + i] = result[i];        }    }
     从上边代码中可以看到,我们用了一个临时数组来存储排序好的数据,所以归并排序的空间复杂度为O(n)。合并的时间度是O(n)。

     

    分析完归并后,我们再来看分解。第一步:先把原始数列除以2分为A,B两个部分,然后再将A,B各自一分为二,重复此过程,直到数列只有一个数据了,这意味着这个数列是有序的。可以明显看到,这个过程是一个递归过程,所以用递归方法实现。

     归并排序的核心代码

public class MergeSort {    //将待排序数组分解    public static void mergeSort(int[] nums, int left, int right, int[] result) {        if (left < right) {            int mid = (left + right) / 2;            mergeSort(nums, left, mid, result);            mergeSort(nums, mid + 1, right, result);            merge(nums, left, mid, right, result);        }    }    //合并有序数组序列,利用辅助数组。    public static void merge(int[] nums, int left, int mid, int right, int[] result) {        int left1 = left;        int right1 = mid;        int left2 = mid + 1;        int right2 = right;        int k = 0;        //先把较小的数移入到临时数组        while (left1 <= right1 && left2 <= right2) {            if (nums[left1] <= nums[left2]) {                result[k++] = nums[left1++];            } else {                result[k++] = nums[left2++];            }        }        //把左边剩余的数移入        while (left1 <= right1) {            result[k++] = nums[left1++];        }        //把右边剩余的数移入        while (left2 <= right2) {            result[k++] = nums[left2++];        }        //将排序好的覆盖到nums数组        for (int i = 0; i < k; i++) {                nums[left + i] = result[i];        }    }}

    归并排序的空间复杂度是O(n),因为其用到了一个大小为n的辅助数组来保存合并排序。

     在这里我们分析一下快速排序的空间复杂度:首先快排使用的空间是O(1)的,但其递归调用会消耗空间,因为递归栈(全局)的深度是O(logn)。所以其最优的空间复杂度为O(logn)。最差的情况下空间复杂度为:O(n):退化为冒泡排序的情况。

    从空间复杂度来看:堆排序最好O(1),其次是快速排序O(logn),最后是归并排序O(n)。

    从稳定性来看:应选归并排序,因为快速排序和堆排序都是不稳定的。

     堆排序,快排,归并排序的时间复杂度都是O(nlong)。快排最坏情况下是O(n^2)--退化为冒泡的时候。

     综合来看,选快速排序。

    


    

    

 

1 0
原创粉丝点击