數據結構和算法自己解決的是「快」和「省」的問題,即如何讓代碼運行的更快,如何讓代碼更省儲存空間。 因此複雜度分析是整個算法學習的精髓。算法
算法的執行效率,就是算法代碼執行的時間。來估算一下下面代碼的執行時間。數組
function sum(n) {
let total = 0;
for (let i=0;i < n;i++) {
total = total + i
}
return total;
}
複製代碼
假設每行代碼執行的時間都同樣,都是unit_time。bash
第 2 行代碼須要 1 個unit_time 的執行時間,第 三、4 行都運行了 n 遍,因此須要 2n*unit_time 的執行時間,因此這段代碼總的執行時間就是 (2n+1)*unit_time。數據結構
能夠看出,代碼的執行時間T(n) 與每行代碼的執行次數成正比數據結構和算法
再來推導下面代碼的運行時間:學習
function sum(n) {
let total = 0;
for (let i=1;i<=n;i++) {
for (let j=1;j<=n;j++) {
total = total + i * j
}
}
return total
}
複製代碼
一樣第2行須要 1個 unit_time 的執行時間,第3行代碼循環執行了n遍,須要n * unit_time的執行時間,第四、5行執行了n² 遍,因此須要(2n²+n+1)*unit_time 的執行時間。ui
儘管咱們不知道unit_time的具體值,但經過兩段代碼的執行時間推導過程,咱們獲得一個很是重要的規律,那就是,全部代碼的執行時間T(n) 與每行代碼的執行次數n成正比。spa
T(n) = O(f(n))
複製代碼
T(n) 表明代碼執行的時間,n表示數據規模的大小;f(n)表示每行代碼執行的次數總和。公式中的O,表示代碼的執行時間T(n) 與f(n) 表達式成正比。code
大O時間複雜度實際上並不具體表示代碼真正的執行時間,而是表示***代碼執行時間隨數據規模增長變化的趨勢***,因此,也叫作漸進時間複雜度。cdn
當n很大時,公式中的低階、常量、係數三部分並不左右增加的趨勢,因此能夠忽略。咱們只須要記錄一個最大量級就能夠了,因此上面兩段代碼就能夠計做:T(n) = O(n) 和 T(n) = O(n²)。
O(1)只是常量級時間複雜度的一種表示方法,並非指只執行了一行代碼,好比下面這段代碼,即便有3行,他的時間複雜度也是O(1),而不是O(3)。
let i = 2;
let j = 5;
let sun = i + j;
複製代碼
通常狀況下,只要算法中不存在循環語句、遞歸語句,即使有成千上萬行代碼,其時間複雜度也是O(1)。
下面這段代碼的時間複雜度爲O(n),由於循環體中的代碼須要執行n次。
let sum = 0;
for (let i=0;i<n;i++){
sum = sum +i
}
複製代碼
咱們要分析算法的複雜度,關鍵就是要分析循環結構的運行狀況。上面代碼由於循環體中代碼須要執行n次,因此時間複雜度爲O(n)。
對數階時間複雜度很是常見。
let i = 1;
while (i <= n) {
i = i * 2
}
複製代碼
從上面代碼中可用看出,遍歷從1開始,每循環一次就乘以2,當i大於n時循環結束。因爲2的x次方等於n,x = log2n,這個循環的時間複雜度就是O(log2n)。無論是以2爲底,仍是以3爲底,咱們均可以把全部對數階的時間複雜度計做:O(logn)。
for (let i=0;i<n;i++) {
for (let j=o;j<n;j++) {
/*時間複雜度爲O(1)的程序步驟*/
}
}
複製代碼
根據循環的時間複雜度等於循環體的複雜度乘以該循環運行的次數。因此上面代碼的時間複雜度爲O(n²)。
通常狀況下,隨着n的增大,T(n) 增加最慢的算法爲最優算法。
咱們查找一個有n個隨機數字數組中的某個數字,最好的狀況是數組的第一個數字就是,那麼算法的時間複雜度爲O(1),但也可能這個數字在最後一位,那麼算法的時間複雜度就是O(n),這就是最壞狀況了。
最壞狀況運行時間是一種保證,那就是運行時間不會再壞了。一般除非特別指定,咱們提到的運行時間都是最壞狀況的運行時間。
本文摘自極客時間王爭老師的 數據結構與算法之美
摘自《大話數據結構》