【数据结构与算法】归并排序的写法

37 阅读1分钟

归并排序

归并排序是一种O(nlogn)时间复杂度的排序算法,空间复杂度O(n),优点是稳定的,并且最坏时间复杂度也是O(nlogn)。

归并的思路

归并就是将两个有序的数组合成一个有序的数组。这一操作可以使用双指针。 归并排序有两个阶段:

  • 切分:先折半把数组分成两个部分,递归下去,直到所有数字分成单独的一个。
  • 合并:利用双指针算法,将两个有序数组合并为一个有序数组

代码示例

func MergeSort(a []int, l int, r int) {
   if l < r {
      // 切分
      mid := (l + r) >> 1
      MergeSort(a, l, mid)
      MergeSort(a, mid+1, r)
      // 合并
      tmp := make([]int, 0, r-l+1)
      i, j := l, mid+1
      for i <= mid && j <= r {
         if a[i] <= a[j] {
            tmp = append(tmp, a[i])
            i++
         } else {
            tmp = append(tmp, a[j])
            j++
         }
      }
      for i <= mid {
         tmp = append(tmp, a[i])
         i++
      }
      for j <= r {
         tmp = append(tmp, a[j])
         j++
      }
      for i := 0; i < len(tmp); i++ {
         a[l+i] = tmp[i]
      }
   }
}

归并排序的非递归写法

还是可以使用队列,bfs进行合并的操作

链表归并排序

链表的归并比快排好写多了,对于两个阶段:

  • 链表分解:可以使用获取链表中间节点的模板
  • 链表合并:也是常见的模板,我们可以直接使用

归并排序解决逆序对问题

  • 逆序对:下标i,j,其中i<j,如果a[i]>a[j],则这是一个逆序对 逆序对可以直接双重循环保留枚举,复杂度O(n^2)。 利用归并排序,可以对其进行优化:
  • 在合并两有序数组的时候,双指针i,j都是从前往后移动的,当a[i]>a[j],说明下标i到mid的数都大于a[j],这样子一下子找到mid-i+1个逆序对,都是以j为结尾的。
  • 在递归过程中,总逆序对数=左半部分逆序对数+右半部分逆序对数+左右合并的逆序对数