歸併排序,合併有序數組,逆序對個數

歸併排序,合併有序列表,求逆序對個數

之因此將標題中三者放一塊兒是由於它們有密不可分的關係.python

合併有序列表

  1. 定義一個空列表 li 用來存放排序後的值;
  2. 定義兩個 cursor lc 和 rc,分別指向左右列表的首部;
  3. 比較 lc 和 rc 指向的值,將較小的值放入 li,同時將指向較小值得遊標右移一位;
  4. 循環上一步,直到某個遊標指向最後;這時左右列表其中一個的所有值已經被加入到 li 中;
  5. 將另一個列表中的剩餘值加入到 li 中.
def merge_ordered_list(left, right):
    res = []
    lc = rc = 0
    while lc < len(left) and rc < len(right):
        if left[lc] <= right[rc]:
            res.append(left[lc])
            lc += 1
        else:
            res.append(right[rc])
            rc += 1
    res.extend(left[lc:])
    res.extend(right[rc:])
    return res

由以上代碼段能夠看出,合併過程當中只對左右列表分別進行了一遍歷,所以時間複雜度爲 O(n)app

歸併排序

歸併排序分爲兩步:code

  1. 將數據儘可能平均分爲左右兩部分;
  2. 對左右兩部分分別進行排序(遞歸調用);
  3. 將左右兩部分合並,見上節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]組成了逆序對,由此可得出逆序對個數,代碼以下:it

merge_ordered_list進行稍許修改,記錄逆序對個數.io

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]:
            res.append(left[lc])
            lc += 1
        else:
            res.append(right[rc])
            rc += 1
            # 統計逆序對個數
            inversion_count += len(left[lc:])
    res.extend(left[lc:])
    res.extend(right[rc:])
    return res

這時調用merge_sort會同時得出li中逆序對個數,時間複雜度爲歸併排序的複雜度O(nlg(n)).class

相關文章
相關標籤/搜索