排序算法之冒泡排序及其優化

冒泡排序


其餘排序方法:選擇排序冒泡排序歸併排序快速排序插入排序希爾排序html


思想

比較相鄰兩個元素,若是前面的元素比後面的元素大,則交換位置。最後一個元素一定會是最大值。
排除掉最後一位元素,繼續循環,直至沒有元素須要比較python

能夠看出,冒牌排序其實和選擇排序時很像的,只不過選擇排序是比較數據,先記錄下最小/大值的下標,等一趟循環結束的時候再交換位置;
而冒泡排序在比較數據的同時也作了交換。算法

性能

時間複雜度與選擇排序同樣,時間複雜度爲O(n^2)。
因爲它們的比較次數同樣,而冒泡排序的交換次數多,因此通常冒泡排序會比選擇排序慢。
但冒泡排序有一個優點,那就是每通過一次循環,數組的有序性就會變高。咱們能夠利用這點來優化冒泡排序,優化方法請看下文。數組

代碼

普通冒泡排序的Python代碼:性能

# 冒泡排序
def bubbleSort(arr):
    size = len(arr)
    for i in range(size - 1, 0, -1):
        for j in range(i):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]

優化

優化一

咱們會發現若是數組一開始就是有序的或者再通過前幾回循環的時候就已經變得有序了,但上述程序仍是會繼續循環比較。針對這一點咱們能夠進行一次優化:優化

# 冒泡排序(優化一)
def bubbleSort(arr):
    size = len(arr)
    for i in range(size - 1, 0, -1):
        # 設一個flag,True表示沒有交換
        hasNoChange = True
        for j in range(i):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                # 交換數據,設爲False
                hasNoChange = False

        # 若是沒有交換,證實數組已經有序了,能夠結束循環
        if hasNoChange: break

通過優化以後,最好的狀況下,時間複雜度爲O(n)。code

優化二

優化一針對的是整個數組已經有序的狀況。那若是一個數組,假設有一萬個數據,其實從一開始或者幾回循環以後,後5千個數據已是有序的且比前5千個數據要大,
在上述代碼中,每次循環仍然會循環到上次循環找出的最大值的前一位。針對這一點,咱們還能夠再作一次優化:htm

# 冒泡排序(優化二)
def bubbleSort(arr):
    size = len(arr)
    # 最後一次交換的位置
    lastChangedIdx = size - 1
    for i in range(len(arr), 0, -1):
        tmpIdx = -1
        # 只須要比較到上次交換的位置
        for j in range(lastChangedIdx):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                tmpIdx = j

        # 若是沒有交換,證實數組已經有序了,能夠結束循環
        if tmpIdx == -1:
            break
        # 若是有交換,將最後一次交換的位置賦給lastChangedIdx
        else:
            lastChangedIdx = tmpIdx

優化三

假如仍是剛纔那個數組,後五千個數據已經有序,但最後一位是整個數組中的最小值,咱們會發現優化二中的優化根本起不了做用。
那想要讓優化二起做用,那麼就須要把那個最小值拿到前面來,有什麼辦法呢?很簡單,就是來一次反向冒泡就好了。
這樣的算法被叫作雙向冒泡排序(也叫雞尾酒排序),爲了高雅地裝逼,咱們就叫它雞尾酒排序吧。blog

# 冒泡排序(優化三,雞尾酒排序)
def bubbleSort(arr):
    size = len(arr)
    # 正向最後一次交換的位置
    rightLastChangedIdx = size - 1
    # 反向最後一次交換的位置
    leftLastChangedIdx = 0
    for i in range(size >> 1):
        tmpIdx = -1
        # 正向冒泡:從反向最後一次交換的位置到正向最後一次交換的位置
        for j in range(leftLastChangedIdx, rightLastChangedIdx):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                tmpIdx = j

        # 若是沒有交換,證實數組已經有序了,能夠結束循環
        if tmpIdx == -1:
            break
        # 若是有交換,將最後一次交換的位置賦給rightLastChangedIdx
        else:
            rightLastChangedIdx = tmpIdx

        tmpIdx = -1
        # 反向冒泡:從正向最後一次交換的位置到反向最後一次交換的位置
        for k in range(rightLastChangedIdx, leftLastChangedIdx, -1):
            if arr[k] < arr[k - 1]:
                arr[k], arr[k - 1] = arr[k - 1], arr[k]
                tmpIdx = k

        # 若是沒有交換,證實數組已經有序了,能夠結束循環
        if tmpIdx == -1:
            break
        # 若是有交換,將最後一次交換的位置賦給leftLastChangedIdx
        else:
            leftLastChangedIdx = tmpIdx

其餘排序方法:選擇排序冒泡排序歸併排序快速排序插入排序希爾排序排序

相關文章
相關標籤/搜索