排序算法之归并排序及其优化
归并排序
其他排序方法:选择排序、冒泡排序、归并排序、快速排序、插入排序、希尔排序、堆排序
思想
归并排序,顾名思义,两个有序的数组合并成一个有序的数组。只要数组的长度大于1,都可以先分为两个数组,并将这两个子数组排好序再合并。
性能
归并排序时稳定的排序算法,平均时间复杂度和最好最坏时间复杂度均为O(nlogn)。
代码
Python代码:
# 归并排序
# left为归并区域的第一个下标,right为归并区域的最后一个下标
def mergeSort(arr, left, right):
if left >= right: return
# 中间下标
mid = left + right >> 1
# 归并当前区域的左半边区域
mergeSort(arr, left, mid)
# 归并当前区域的右半边区域
mergeSort(arr, mid + 1, right)
# 合并左右两边区域并排序
merge(arr, left, mid, right)
# 合并左右两边区域并排序
# left为左边区域的第一个下标,mid为左边区域的最后一个下标
# mid+1为右边区域的第一个下标,right为右边区域的最后一个下标
def merge(arr, left, mid, right):
# 排序需要借助一个数组
tmpArr = []
i, j = left, mid + 1
# 同时循环左右两个区域,小的数先移进tmpArr,直到其中一个区域没有数据
while i <= mid and j <= right:
if arr[i] <= arr[j]:
tmpArr.append(arr[i])
i += 1
else:
tmpArr.append(arr[j])
j += 1
# 若左边区域还有数据,则直接添加到后面
while (i <= mid):
tmpArr.append(arr[i])
i += 1
# 若右边区域还有数据,则直接添加到后面
while (j <= right):
tmpArr.append(arr[j])
j += 1
# 将合并排好序的数据复制到原数组
for x in tmpArr:
arr[left] = x
left += 1
优化
我们会发现如果左右数组的数据刚好有序,那上述代码中,仍会将数据先复制到tmpArr,再复制回原数组。
只要在merge方法前加上下面的判断,就能使最好时间复杂度降为O(n):
# 右边区域的值全部比左边区域的值大时,则不需要进行合并排序
if arr[mid] <= arr[mid + 1]: return arr
# 归并排序
# left为归并区域的第一个下标,right为归并区域的最后一个下标
def mergeSort(arr, left, right):
if left >= right: return
# 中间下标
mid = left + right >> 1
# 归并当前区域的左半边区域
mergeSort(arr, left, mid)
# 归并当前区域的右半边区域
mergeSort(arr, mid + 1, right)
# 合并左右两边区域并排序
merge(arr, left, mid, right)
# 合并左右两边区域并排序
# left为左边区域的第一个下标,mid为左边区域的最后一个下标
# mid+1为右边区域的第一个下标,right为右边区域的最后一个下标
def merge(arr, left, mid, right):
# 右边区域的值全部比左边区域的值大时,则不需要进行合并排序
if arr[mid] <= arr[mid + 1]: return arr
# 排序需要借助一个数组
tmpArr = []
i, j = left, mid + 1
# 同时循环左右两个区域,小的数先移进tmpArr,直到其中一个区域没有数据
while i <= mid and j <= right:
if arr[i] <= arr[j]:
tmpArr.append(arr[i])
i += 1
else:
tmpArr.append(arr[j])
j += 1
# 若左边区域还有数据,则直接添加到后面
while (i <= mid):
tmpArr.append(arr[i])
i += 1
# 若右边区域还有数据,则直接添加到后面
while (j <= right):
tmpArr.append(arr[j])
j += 1
# 将合并排好序的数据复制到原数组
for x in tmpArr:
arr[left] = x
left += 1