資料結構與演算法系列 時間 空間複雜度

2021-09-11 04:29:25 字數 3074 閱讀 9085

資料結構和演算法本質就是幫我們用最快的時間和最少的空間來執行我們的**。所以,執行效率是衡量乙個演算法的非常重要的指標。那如何來計算你的演算法**的執行效率呢?這就需要時間、空間複雜度來分析了。

有人可能會說,我把**執行一遍,然後通過統計、監控就能知道執行的時間和需要的記憶體大小。幹嘛還需要時間、空間複雜度來分析呢?我都能得到具體需要的時間和記憶體了,還需要多此一舉嗎?

首先,這種評估演算法效率的方法沒有問題,我們還給這種方法起了乙個名字,叫事後統計法。但是這種方法有很大的侷限性。

1. 測試結果受測試環境影響

測試環境的硬體對測試結果有非常大的影響。比如,同樣的**在i7和i5的機器上執行,結果肯定是不同的。還有可能在一台機器上a**比b**執行速度要快,我們換另外一台機器卻得到相反的結果。

2. 測試結果受資料規模的影響

比如排序演算法,原始資料如果有序度不一樣,執行的時間就會有很大的差別。原始資料規模的大小不同,也可能會讓原來速度快的演算法變成速度慢的。

所以我們就需要乙個不需要具體的測試資料來測試,就可以大概估算出執行效率的方法,就是時間、空間複雜度分析方法。

大o複雜度表示方法

我們通過度**,來估算出它執行所需要的時間,下邊來看一段具體的**。

public

intfunction

(int n)

return sum;

}

這裡我們假設每一行**執行的時間都是相同的為 t,那麼第 3 行執行的時間為 t,第 4 和 6 行執行了 n 次,需要時間為 2n*t,總的時間為 (1+2n)*t,可以看出來總的**的執行時間 t(n) 與每行**的執行次數成正比。

然後我們再來看下邊的**

public

intfunction

(int n)

}return sum;

}

這段**中在上邊的基礎上又套了一層 for 迴圈,第 6 和 8 行執行了 n^2 次,需要的時間為 2n^2 * t ,總的需要的時間為 t(n)=(2n^2+2n+1) * t

通過上述兩個具體的**例子我們總結出乙個公式: t(n)=o(f(n))

t(n) 表示代買執行的時間,n 是資料的大小,f(n) 表示**執行的總次數,o 表示公式中 t(n) 與 f(n) 成正比。這就是大 o 時間複雜度表示法,它實際上並不表示**具體執行所需要的時間,它表示隨著資料規模的變化**執行時間的變化趨勢。

當 n 非常大時,低階、係數、常量對結果的影響就非常小了,所以我們可以把這幾項忽略不記,只保留最高端的就可以了,所以上邊兩個例子中 o(1+2n) 就可以記為 o(n),o(2n^2+2n+1) 就可以記為 o(n^2)。

上邊我們知道了怎麼用大 o 時間複雜度的表示方法。那麼我們如何具體分析一段**的時間複雜度呢?

因為大 o 時間複雜度只表示一種變化的趨勢,所以我們只需要關心階數最高的那部分就可以了,低階、係數、常量我們都可以忽略。以上邊第一段**為例 o(1+2n),我們忽略掉係數和常量最後就得到了 o(n)。

對於順序執行的長**,我們把它分成幾部分,分別求出其總時間 t(n) ,然後相加得到總的時間,最後同樣忽略低階、係數、常量部分,保留最高端的部分然後得出最後的時間複雜度大 o。

對於邏輯複雜的巢狀**,我們分別求巢狀內外**的複雜度,然後相乘得出最終的時間複雜度大 o。

幾種常見的時間複雜度分析

下邊列舉出常見的時間複雜度量級

多項式量級

非多項式量級

常數階 o(1)

指數階 o(2^n)

對數階 o(logn)

階乘階 o(n!)

線性階 o(n)

線性對數階 o(nlogn)

k次方階 o(n^2) o(n^3) o(n^k)

對於非多項式量級的演算法會隨著資料規模的增大急劇增加,所以分多項式量級的演算法是非常低效的,我們不做過多的介紹。主要來介紹幾種常見的多項式量級的時間複雜度。

常數階 o(1)

o(1) 只是常量級時間複雜度的表示方式,不是只有一行**,而是每一段**的執行時間不隨著 n 的資料規模的變大而變長,這樣的**的時間複雜度記為 o(n)。

對數階 o(logn) o(nlogn)

對數階複雜度很常見,但是分析的時候卻不容易,下面我們用一段實際的例子來看看對數階時間複雜度。

public

void

function

(int n)

}

通過上邊我們總結的方法,我們只需要知道 while 迴圈的次數,就能得到這段**的複雜度。從**中可以看出 i 的值從 1 開始,每迴圈一次乘以 2,直到 i 的值大於 n 的時候結束,我們得到規律然後把結果列出來,

2^0 2^1 2^3 2^4 …… 2^x = n ,然後求得執行的次數 x = logn ,這段**的時間複雜度就是 o(logn)。

o(n+m) o(n*m)

我們再來講一種跟前面都不一樣的時間複雜度,**的複雜度由兩個資料的規模決定,所以我們需要同時考慮兩種資料規模對結果的影響,如果是順序的,那麼時間複雜度就為 o(n+m),如果為巢狀的那麼時間複雜度為 o(n*m)。

空間複雜度分析

理解了上邊的時間複雜度的分析方法,空間複雜度的分析也就很簡單了。空間複雜度表示演算法的儲存空間與資料規模之間的增長關係。

同樣我們通過一段實際的**來分析一下。

public

void

function

(int n)

}

我們看到第 3 行**,我們申請了乙個空間儲存變數 i ,但是這個是常數階的,不會隨 n 的變化而變化,所以可以忽略,第 4 行我們申請了乙個大小為 n 的 int 型別陣列,除此之外其餘**沒有占用其他的空間,所以這段**的空間複雜度為 o(n)。

我們常見的空間複雜度有 o(1)、o(n)、o(n^2),對數階的空間複雜度一般情況下用不到。所以空間複雜度比時間複雜度容易分析的多,我們也只需要掌握常見的幾種就可以了。

最後我們總結一下常見的幾種複雜度,執行效率從高到低依次為

o(1)>o(logn)>o(n)>o(nlogn)>o(n^2)

資料結構與演算法系列二(複雜度分析)

有人說,資料結構與演算法,計算機網路,與作業系統都一樣,脫離日常開發,除了面試這輩子可能都用不到呀!有人說,我是做業務開發的,只要熟練api,熟練框架,熟練各種中介軟體,寫的 不也能 飛 起來嗎?於是問題來了 為什麼還要學習資料結構與演算法呢?理由一 面試的時候,千萬不要被資料結構與演算法拖了後腿 ...

資料結構與演算法 演算法的時間複雜度和空間複雜度

無論 執行了多少行,只要是沒有迴圈等複雜結構,那這個 的時間複雜度就都是o 1 如 int i 1 int j 2 i j int m i j 上述 在執行的時候,它消耗的時候並不隨著某個變數的增長而增長,那麼無論這類 有多長,即使有幾萬幾十萬行,都可以用o 1 來表示它的時間複雜度。例如有乙個迴圈...

資料結構與演算法系列 字典樹

一 背景 什麼是字典樹?trie樹,即字典樹,又稱單詞查詢樹或鍵樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計和排序大量的字串 但不僅限於字串 所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是 最大限度地減少無謂的字串比較,查詢效率比雜湊表高。trie的核心思想是空間換時間。利用字串的...