归并排序
归并排序是一种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为结尾的。 - 在递归过程中,总逆序对数=左半部分逆序对数+右半部分逆序对数+左右合并的逆序对数