Recca Chao 的 gitHub page

推廣網站開發,包含 Laravel 和 Kotlin 後端撰寫、自動化測試、讀書心得等。Taiwan Kotlin User Group 管理員。

View on GitHub

Binary Search 的時間複雜度

以下我們嘗試計算 Binary Search 的時間複雜度

範例程式碼

Binary Search 用 Kotlin 程式語言的寫法為

fun search(nums: IntArray, target: Int): Int {
    var left = 0
    var right = nums.size - 1
    while (left <= right) {
        val pivot = left + (right - left) / 2
        when {
            nums[pivot] == target -> return pivot
            nums[pivot] > target -> right = pivot - 1
            nums[pivot] < target -> left = pivot + 1
        }
    }
    return -1
}

假設

這邊我們有幾個基本假設

因為處理的數字均是 Int 有固定大小(4 Byte)

我們假設針對這些輸入進行變數設置、比較大小、回傳、或者對輸入進行四則運算等等

所花費的時間均為常數。

計算時間複雜度

我們已經假設了設置變數的時間為常數

所以

var left = 0
var right = nums.size - 1

還有

return -1

這段時間均為常數,我們假設總和為 C_1

由於四則運算的時間複雜度為常數,while 迴圈的內容內

val pivot = left + (right - left) / 2

這段時間也是常數

接著我們看 when

when {
    nums[pivot] == target -> return pivot
    nums[pivot] > target -> right = pivot - 1
    nums[pivot] < target -> left = pivot + 1
}

這邊針對固定個數的情境進行比較

這些比較的時間不因輸入陣列的大小有所改變

所以我們可以確定這邊的時間複雜度也會是常數

迴圈內的所有邏輯所需時間總和,我們將這段時間假設為 C_2

所以我們比較需要關注的問題

就會是

while (left <= right) {
}

這個迴圈需要運行的次數,究竟是幾次

while 迴圈次數

我們假設輸入的陣列 size 為 n

val pivot = left + (right - left) / 2
when {
    nums[pivot] == target -> return pivot
    nums[pivot] > target -> right = pivot - 1
    nums[pivot] < target -> left = pivot + 1
}

我們發現,在每一次迴圈內,不管是哪種情境

我們至少會將需要檢查的元素個數

減少到 n/2 以下,直到只剩下 1 個元素。

也就是説,迴圈運作的次數

等同於不斷將 n 除以 2,直到 n 等於 1 時的次數

也就是 log2n

因此,我們就可以知道

這個演算法的時間複雜度 f(x)

是 C_1 + log2x × C_2

計算 Big-O-notation

已知 f(x) = C_1 + log2x × C_2

我們令 x_0 = 2^C_1

M = 2 × C_2 + 1

對所有輸入大小 x > x_0,

M × log2x = 2 × C_2 × log2x + log2x

由於 log2x > log2x_0 = C_1

所以 2 × C_2 × log2x + log2x > C_1 + 2 × C_2 × log2x

由於 C_2 > 0

所以 2 × C_2 × log2x > C_2 × log2x

綜合以上可以得到

M × log2x > C_1 + C_2 × log2x

得證 f(x) = O(log x)

延伸閱讀