歸并排序
歸并排序體現的是分治的思想,將一個數組一分為二,剩余的兩部分再一分為二,以此遞歸,直到不能分解為止。然后順序合并,合并的過程中,比較要合并的兩端,形成一個新的有序數組。
代碼實現
直接修改原數組的方式
"""
歸并排序
Author: xingrui
"""
# 歸并排序 - 正序
def mergeSort2(nums: list, p: int, r: int, direction: str):
if p >= r:
return
q = int((r + p) / 2)
mergeSort2(nums, p, q, direction)
mergeSort2(nums, q + 1, r, direction)
merge(nums, p, q , r, direction)
# 合并數組
def merge(nums: list, p: int, q: int, r: int, direction: str):
i = p
j = q + 1
res = []
if direction == 'asc':
while i <= q and j <= r:
if nums[i] <= nums[j]:
res.append(nums[i])
i += 1
else:
res.append(nums[j])
j += 1
else:
while i <= q and j <= r:
if nums[i] >= nums[j]:
res.append(nums[i])
i += 1
else:
res.append(nums[j])
j += 1
while i <= q:
res.append(nums[i])
i += 1
while j <= r:
res.append(nums[j])
j += 1
while p <= r:
nums[p] = res[0]
p += 1
res = res[1:]
if __name__ == "__main__":
nums = [4, 5, 2, 6, 2, 3, 9, 3, 1, 20, 57, 39]
p, q = 0, len(nums) - 1
mergeSort2(nums, p, q, 'asc')
print('歸并排序,正序排列', nums)
mergeSort2(nums, p, q, 'desc')
print('歸并排序,逆序排列', nums)
生成新數組
"""
歸并排序
Author: xingrui
"""
# 歸并排序 - 正序
def mergeSort(nums: list, direction: str) -> list:
if len(nums) <= 1:
return nums
else:
mid = int(len(nums) / 2)
left = mergeSort(nums[:mid], direction)
right = mergeSort(nums[mid:], direction)
return merge(left, right, direction)
# 合并數組
def merge(left: list, right: list, direction: str) -> list:
res = []
if direction == 'asc':
while left and right:
minValue = left[0]
if left[0] < right[0]:
left = left[1:]
else:
minValue = right[0]
right = right[1:]
res.append(minValue)
else:
while left and right:
maxValue = left[0]
if left[0] > right[0]:
left = left[1:]
else:
maxValue = right[0]
right = right[1:]
res.append(maxValue)
res += left if left else right
return res
# 合并數組
# def merge(left: list, right: list, direction: str) -> list:
# res = []
#
# if direction == 'asc':
# while left and right:
# minValue = left[0]
# if left[0] < right[0]:
# left.remove(left[0])
# else:
# minValue = right[0]
# right.remove(right[0])
#
# res.append(minValue)
# else:
# while left and right:
# maxValue = left[0]
# if left[0] > right[0]:
# left.remove(left[0])
# else:
# maxValue = right[0]
# right.remove(right[0])
#
# res.append(maxValue)
#
# res += left if left else right
# return res
# 合并數組
# def merge(left: list, right: list, direction: str) -> list:
# res = []
#
# if direction == 'asc':
# while left and right:
# minValue = left.pop(0) if left[0] < right[0] else right.pop(0)
# res.append(minValue)
# else:
# while left and right:
# maxValue = left.pop(0) if left[0] > right[0] else right.pop(0)
# res.append(maxValue)
#
# res += left if left else right
# return res
if __name__ == "__main__":
nums = [4, 5, 2, 6, 2, 3, 9, 3, 1, 20, 57, 39]
print('歸并排序,正序排列', mergeSort(nums, 'asc'))
print('歸并排序,逆序排列', mergeSort(nums, 'desc'))
分析
時間復雜度
歸并排序是使用遞歸實現的,推導排序的時間復雜度不太容易。可以先列個公式:
T(n) = 2 * T(n/2) + n
其中T(n)是整個歸并排序所需要花費的時間,歸并排序需要將數組一分為二,分開部分排序需要的時間為T(n/2),最后的n是當數組分到不可再分的時候,這個時候就需要merge所有最小片段,這個過程的時間復雜度為O(n)。
得到了公式,我們就可以按照歸并排序的規則——一分為二,繼續分解公式
T(n) = 2 * T(n/2) + n
T(n) = 2 * 2 * T(n/4) + 2n = 4 * T(n/4) + 2n
T(n) = 4 * 2 * T(n/8) + 3n = 8 * T(n/8) + 3n
T(n) = 8 * 2 * T(n/16) + 4n = 16 * T(n/16) + 4n
..............
T(n) =
上面的式子一直分解,直到T(n) = 1的時候不再分解,此時,
則,然后將k代入上面的公式
根據時間復雜度的最大原則, 大于 n,則歸并排序的時間復雜度為
空間復雜度
歸并排序的空間復雜度為O(n),因為在合并的時候需要一個新數組來裝臨時數據,這個數組的長度最大不會超過數組長度n
是否穩定
歸并排序是穩定的排序算法,決定其是否穩定的代碼在合并函數中
while i <= q and j <= r:
if nums[i] <= nums[j]:
res.append(nums[i])
i += 1
else:
res.append(nums[j])
j += 1
只要確保遇到相等的值,總是優先取左邊的數據,該算法就是穩定的。
文章參考
https://blog.csdn.net/czg13548930186/article/details/72860942
http://www.lxweimin.com/p/22e7d281859e
https://oiltang.com/2014/05/04/markdown-and-mathjax/
https://time.geekbang.org/column/article/41913