• 数据结构 归并排序


      归并排序采用了分治的思想(分治法将问题分(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

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

      归并排序求逆序对

  • 相关阅读:
    js 时间相关函数
    javascript面向对象:继承、多态
    面向对象相关
    reset.css css重置公共样式
    开通博客园第一天。
    vue 和 react 路由跳转和传参
    前端密码加密方式
    react组件回顶部
    移动端使用rem方法
    用rekit创建react项目
  • 原文地址:https://www.cnblogs.com/WJQ2017/p/8343714.html
Copyright © 2020-2023  润新知