数据结构 归并排序

Posted 王景迁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 归并排序相关的知识,希望对你有一定的参考价值。

  归并排序采用了分治的思想(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。归并指将两个或两个以上的有序表组合成一个新的有序表。假设待排序表有n个元素,看成是n个有序的子表,每个子表长度为1,然后两两归并,得到n/2个长度为2或1的有序表,继续两两归并,直到合并成一个长度为n的有序表为止,这种排序方法称为2路归并排序。

  

  合并相邻有序子序列

  将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8]。

  

  

  求逆序数:归并排序将数组a[l,h]分成两半a[l,mid]和a[mid+1,h]后分别进行归并排序,然后再将这两半合并起来。在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,不产生逆序数;当a[i]>a[j]时,在前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并排序的合并过程中计算逆序数。

  题目来源:数组中的逆序对

 1 public class MergeSort {
 2     // 待排序数组
 3     private int[] arr;
 4     // 逆序对总数
 5     private int result;
 6     
 7     // 归并两个数组
 8     private void merge(int left, int mid, int right) {
 9         int i = left, j = mid + 1, k = 0;
10         int[] tempArr = new int[right - left + 1];
11         
12         while (i <= mid && j <= right) {
13             if (arr[i] > arr[j]) {
14                 tempArr[k++] = arr[j++];
15                 result += mid + 1 - i;
16                 // 避免溢出
17                 result %= 1000000007;
18             } else {
19                 tempArr[k++] = arr[i++];
20             }
21         }
22         
23         while (i <= mid) {
24             tempArr[k++] = arr[i++];
25         }
26         while (j <= right) {
27             tempArr[k++] = arr[j++];
28         }
29         
30         System.arraycopy(tempArr, 0, arr, left, k);
31     }
32     
33     // 归并排序
34     private void mergeSort(int left, int right) {
35         if (left < right) {
36             int mid = (left + right) >> 1;
37             mergeSort(left, mid);
38             mergeSort(mid + 1, right);
39             merge(left, mid, right);
40         }
41     }
42     
43     // 获取逆序数
44     public int getResult(int[] array) {
45         if (array == null || array.length <= 1) {
46             return 0;
47         }
48         
49         arr = array;
50         result = 0;
51         mergeSort(0, arr.length - 1);
52         
53         return result;
54     }
55 }

  测试用例:

 1 import static org.junit.Assert.*;
 2 
 3 import org.junit.Before;
 4 import org.junit.Test;
 5 
 6 public class MergeSortTest {
 7     MergeSort mergeSort;
 8     
 9     @Before
10     public void setUp() throws Exception {
11         mergeSort = new MergeSort();
12     }
13 
14     @Test
15     public void test() {
16         int[] arr = {1, 2, 3, 4, 5, 6, 7, 0};
17         assertEquals(7, mergeSort.getResult(arr));
18     }
19 
20 }

  测试结果:

  

  2路归并排序性能分析:

  空间复杂度:2个有序表归并时最多需要n个单元的辅助空间,所以空间复杂度为O(n)。

  时间复杂度:每一趟归并的时间复杂度为O(n),需要log2n趟归并,所以时间复杂度为O(nlog2n)。最好、最坏和平均时间复杂度均为O(nlog2n)。

  稳定性:2个有序表归并时不会改变相同元素的相对次序,所以2路归并排序是稳定的。

  Java中Arrays.sort()方法采用了一种名为TimeSort的排序算法,是归并排序的优化版本。

  

  参考资料

  《2017年数据结构联考复习指导》 P304-305

  图解排序算法(四)之归并排序

  归并排序求逆序对

以上是关于数据结构 归并排序的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript算法(归并排序与快速排序)

堆排/快排/希尔/归并排序,谁更快?

快速排序和归并排序的区别,有代码

归并排序和冒泡排序

算法与数据结构:时间复杂度——以归并排序为例

经典排序算法之归并排序