之所以將標題中三者放一起是因為它們有密不可分的關係.
定義乙個空列表 li 用來存放排序後的值;
定義兩個 cursor lc 和 rc,分別指向左右列表的首部;
比較 lc 和 rc 指向的值,將較小的值放入 li,同時將指向較小值得游標右移一位;
迴圈上一步,直到某個游標指向最後;這時左右列表其中乙個的全部值已經被加入到 li 中;
將另外乙個列表中的剩餘值加入到 li 中.
def merge_ordered_list(left, right):
res =
lc = rc = 0
while lc < len(left) and rc < len(right):
if left[lc] <= right[rc]:
lc += 1
else:
rc += 1
res.extend(left[lc:])
res.extend(right[rc:])
return res
由以上**段可以看出,合併過程中只對左右列表分別進行了一遍歷,因此時間複雜度為o(n)
歸併排序分為兩步:
將資料盡量平均分為左右兩部分;
對左右兩部分分別進行排序(遞迴呼叫);
將左右兩部分合併,見上節merge_ordered_list
.
def merge_sort(li):
if len(li) == 1:
return li
# split
mid_index = len(li) // 2
left = merge_sort(li[:mid_index])
right = merge_sort(li[mid_index:])
# merge
return merge_ordered_list(left, right)
因為每次都是平均分的,因此將乙個長度為 n 的列表分為 n 個長度為 1 的子列表需要lg(n)
次操作(可以將拆分過程想象為樹的分叉),因此merge_sort
需遞迴呼叫 n 次;
又因為每次呼叫的時間複雜度為o(n)
,故整個過程的時間複雜度為o(nlg(n))
如果採用暴力求解,分別求每個元素逆序對,需要兩兩比較列列表中的元素,時間複雜度為o(n**2)
;
結合歸併排序可以將時間複雜度降為o(nlg(n))
;
在第一節合併有序列表
第三步中,rc 指向元素right[rc]
小於 lc 指向元素left[lc]
時,left[lc:]
中的每個元素都和right[rc]
組成了逆序對,由此可得出逆序對個數,**如下:
對merge_ordered_list
進行稍許修改,記錄逆序對個數.
inversion_count = 0
def merge_ordered_list(left, right):
global inversion_count
res =
lc = rc = 0
while lc < len(left) and rc < len(right):
if left[lc] <= right[rc]:
lc += 1
else:
rc += 1
# 統計逆序對個數
inversion_count += len(left[lc:])
res.extend(left[lc:])
res.extend(right[rc:])
return res
這時呼叫merge_sort
會同時得出li中逆序對個數,時間複雜度為歸併排序的複雜度o(nlg(n))
. 合併有序陣列
時限 1000ms 記憶體限制 10000k 總時限 3000ms 描述 給你兩個有序且公升序的陣列,請你把它們合成乙個公升序陣列並輸出 give you two ordered ascending array,you put them into one ascending array and ou...
合併有序陣列
描述 給你兩個有序整數陣列 nums1 和 nums2,請你將 nums2 合併到 nums1 中,使 num1 成為乙個有序陣列。說明 初始化 nums1 和 nums2 的元素數量分別為 m 和 n 你可以假設 nums1 有足夠的空間 空間大小大於或等於 m n 來儲存 nums2 中的元素。...
合併有序陣列
給你兩個有序整數陣列 nums1 和 nums2,請你將 nums2 合併到 nums1 中,使 nums1 成為乙個有序陣列。說明 初始化 nums1 和 nums2 的元素數量分別為 m 和 n 你可以假設 nums1 有足夠的空間 空間大小大於或等於 m n 來儲存 nums2 中的元素。vo...