記錄我對'咱們有成熟的時間複雜度爲O(n)的算法獲得數組中任意第k大的數'的誤解

這篇博客記錄我對劍指offer第2版"面試題39:數組中出現次數超過一半的數字"題解1的一句話的一個小誤解,以及彙總一下涉及partition算法的相關題目。面試

在劍指offer第2版"面試題39:數組中出現次數超過一半的數字"的解法一(基於partition,且哨兵選擇數組第一個元素)中,有這麼一句話:
咱們有成熟的時間複雜度爲O(n)的算法獲得數組中任意第k大的數字,這句話讓我產生了一點誤解,讓我誤覺得"只須要調用一次partition就能找到第k大的數",可是實際上最差狀況下須要調用n次partition函數才能找到第k大的數。由於partition每次都返回的是哨兵的位置,可是在函數運行過程當中,隨着l,r入參的變化,哨兵(數組首位元素)的位置是隨之變化的,具備不肯定性。算法

因此基於partition獲取數組中任意第k大元素的時間複雜度應該以下:數組

  1. 最好時間複雜度 O(1) : 第一次循環就找到正確的哨兵(即,第k大元素)
  2. 最差時間複雜度 n*O(n): 最後一次才找到正確的哨兵
  3. (加權)平均複雜度 這個我不太會算,估計是O(n)

估算過程:
3.1 加權平均複雜度 = 某機率值*O(n)
3.2 機率值是常數,而後去掉常數項,得O(n)函數

partition的Go代碼以下:code

// partition代碼的時間複雜度是O(n),由於須要經過for循環遍歷數組,並把每一個元素都劃分到大分區或小分區中。
func partition(nums []int, l, r int) int {
    // 1. 哨兵 取第一個元素
    v := nums[l]
    // 2. 大小分區的定義和初始化 [l+1,p]<v && [p+1,cur-1]>v 
    p := l
    // 3. 處理哨兵以後的每個元素
    cur := l + 1
    for ; cur <= r; cur++ {
        if nums[cur] < v {
            nums[cur], nums[p+1] = nums[p+1], nums[cur]
            p++
        }
    }
    nums[p], nums[l] = nums[l], nums[p] // 哨兵和小分區的最後一個元素交換,使得哨兵左邊是小的,右邊是大的.
    return p
}

另外,還有如下這些算法題涉及了partition函數的運用,他們的共同特色都是須要用partition查找xxx位置的數字。索引

  1. Majority Element(本博客討論的題) : partition查找m位置的數(中位數:索引爲n/2)
  2. Kth Largest Element in an Array : partiton查找k位置的數
  3. 劍指offer面試題40:最小的k個數 : partiton查找k位置的數,可是隻返回k位置左邊的數,也就是小於哨兵的那些數
相關文章
相關標籤/搜索