python歸并排序--非遞歸實現

歸并排序的非遞歸實現
非遞歸的實現一直想不明白,直到看了這位大哥的博客,茅塞頓開。非常感謝這位大哥。博客地址:博客地址
遞歸實現是將復雜問題一步一步細分為簡單的子問題,直到最小子問題的時候,開始合并。

image.png

非遞歸剛好相反:從最小子問題開始一步一步解決,直到復雜的問題。


image.png

由圖片可知:
第一次:我們將數組分為 8個子數組 每個數組 1 個元素,對相鄰的兩個數組進行排序合并。
第二次:我們將數組分為 4個子數組 每個數組 2 個元素,對相鄰的兩個數組進行排序合并。
第三次:我們將數組分為 2個子數組 每個數組 4 個元素,對相鄰的兩個數組進行排序合并。
至此:排序完畢。
因此:
第一步:劃分每個子數組元素的個數(也就是子數組的長度)
1. 第一次 每個子數組元素 個數 為 1.
2. 第二次 每個子數組元素 個數 為 2.
3. 第三次 每個子數組元素 個數 為 4.
可以看出來 每個子數組元素個數 以2的倍數遞增

i = 1 #子數組長度
while i < len(seq):
     i *= 2

第二步:對每個相鄰的數組進行排序合并。
假設 seq = [5,4,0,3,1,6,2,7]
首先:求得要合并的兩個相鄰數組的區間 [low:mid) [mid:height)
1. 當子數組長度為 1 的時候 要合并的相鄰兩個數組的區間為:
[0,1) [1,2) [2,3) [3,4) [4,5) [5,6) [6,7) [7,8) 子數組長度為 1
2. 子數組長度為 2 要合并的相鄰兩個數組的區間為:
[0,2) [2,4) [4,6) [6,8) 子數組長度為 2
3. 子數組長度為 4 要合并的相鄰兩個數組的區間為:
[0,4) [4,8) 子數組長度為 4
下面來求 區間中的 low mid height:
1. 當子數組為1的時候:
low mid height 分別等于:
0:1:2 i = 1 low = 0 mid = low + 1 height = low + 2
2:3:4 i = 1 low = 2 mid = low + 1 height = low + 2
4:5:6 i = 1 low = 4 mid = low + 1 height = low + 2
6:7:8 i = 1 low = 6 mid = low + 1 height = low + 2
抽象為: i = 1 low = low + 2 * i mid = low + i height = low + 2i
2. 當子數組為2的時候:
low mid height 分別等于:
0:2:4 i = 2 low = 0 mid = low + 2 height = low + 4
4:6:8 i = 2 low = 4 mid = low + 2 height = low + 4
抽象為: i = 2 low = low + 2 * i mid = low + i height = low + 2 * i
3. 當子數組為4的時候:
low mid height 分別等于:
0:4:8 i = 4 low = 0 mid = low + 4 height = low + 2 i
抽象為: i = 4 low = low + 2
i mid = low + 4 height = low + 2
i
綜上所述:抽象出求low mid height 通用公式:
low = low + 2 x i
mid = low + i
height = low + 2 x i

seq = [5, 4, 3, 0, 1, 2, 7, 5]
i = 1
while i < len(seq):
    print '子數組 長度 : ',i
    low = 0
    while low < len(seq):
        mid = low + i
        height = low + 2*i
        print 'low ',low,'mid:',mid,'height:',height
        low += 2*i
    i *= 2

運行結果如下:


image.png

我們此時的猜想,以及抽象出來的通用公式 在len(seq) == 2的N次方的時候是對的。
我們在換一組不同的數試試 非len(seq) == 2的N的數試試

seq = [5, 4, 3, 0, 1, 2, 7, 5, 11,9]
i = 1
while i < len(seq):
    print '子數組 長度 : ',i
    low = 0
    while low < len(seq):
        mid = low + i
        height = low + 2*i
        print 'low ',low,'mid:',mid,'height:',height
        low += 2*i
    i *= 2

運行結果如下:


image.png

這時候就出錯了。我們數組seq 一共才10個元素,height 已經達到了 12,16.mid 達到了 12.很明顯數組下標越界了。
我們需要做一些調整。
首先解決height 越界的問題。
當數組終結時候 height 最大達到了16,而我們的數組最大才10個元素。
所以我們 從 height 與 len(seq)中選擇一個最小的元素,來作為數組的右邊界。因為數組seq 長度是 10 ,height肯定不能超過 數組長度呀,不然就數組下標越界咯。
改版后的代碼如下:

seq = [5, 4, 3, 0, 1, 2, 7, 5, 11,9]
i = 1
while i < len(seq):
    print '子數組 長度 : ',i
    low = 0
    while low < len(seq):
        mid = low + i
        height = min(low + 2 * i, len(seq))
        print 'low ',low,'mid:',mid,'height:',height
        low += 2*i
    i *= 2

運行結果如下:


image.png

現在height的問題已經解決了。
現在解決 mid 的問題。
mid是用來將一個數組拆分為兩個部分的:[low,mid) [mid,height)
當mid>height的時候,很明顯嘛 [mid,height)已經沒有了,也就是說 數組已經不能再拆分了。不能拆分了我們就不拆分唄,直接pass,總有一天能輪到他,如下圖:


image.png

原諒我畫的丑·····
代碼如下:
seq = [5, 4, 3, 0, 1, 2, 7, 5, 11,9]
i = 1
while i < len(seq):
    print '子數組 長度 : ',i
    low = 0
    while low < len(seq):
        mid = low + i
        height = min(low + 2 * i, len(seq))
        if mid < height:
            print 'low ',low,'mid:',mid,'height:',height
        low += 2*i
    i *= 2

運行結果如下:


image.png

這不 mid越界的問題 已經沒了。
len(seq) 為奇數的時候 同樣適用這樣的處理方式。
下面進行 合并排序。
合并與遞歸模式的合并是一樣的。
代碼如下:

def merge(seq,low,mid,height):
    """合并兩個已排序好的列表,產生一個新的已排序好的列表"""
    # 通過low,mid height 將[low:mid) [mid:height)提取出來
    left = seq[low:mid]
    right = seq[mid:height]
    print 'left:', left, 'right:', right

    k = 0   #left的下標
    j = 0   #right的下標
    result = [] #保存本次排序好的內容
    #將最小的元素依次添加到result數組中
    while k < len(left) and j < len(right):
        if left[k] <= right[j]:
            result.append(left[k])
            k += 1
        else:
            result.append(right[j])
            j += 1
    #將對比完后剩余的數組內容 添加到已排序好數組中
    result += left[k:]
    result += right[j:]
    #將原始數組中[low:height)區間 替換為已經排序好的數組
    seq[low:height] = result
    print "seq:", seq

完整代碼如下:

seq = [5, 4, 3, 0, 1, 2, 7, 5, 11,9]
i = 1
while i < len(seq):
    print '子數組 長度 : ',i
    low = 0
    while low < len(seq):
        mid = low + i
        height = min(low + 2 * i, len(seq))
        if mid < height:
            print 'low ',low,'mid:',mid,'height:',height
            merge(seq,low,mid,height)
        low += 2*i
    i *= 2

運行結果如下:


image.png

image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經驗。 張土汪:刷leetcod...
    土汪閱讀 12,766評論 0 33
  • Note:寫后感:理解算法思想很重要!理解算法思想很重要!理解算法思想很重要!之后嘗試自己獨立碼代碼對算法的理解更...
    Crystalajj閱讀 3,344評論 0 4
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個記錄插入到已排序好...
    依依玖玥閱讀 1,270評論 0 2
  • 喜歡吉他 喜歡畫畫 喜歡跑步 奈何敲不完的代碼 學不會的js
    側珥傾聽閱讀 212評論 0 4
  • 又看了一遍,喜歡這種類型的男主,溫柔,強大,包容
    悠然逛南山閱讀 185評論 0 0