天天看點

如何了解時間複雜度和空間複雜度

簡單說就是,同一個功能

别人寫的代碼跑起來占記憶體 100M,耗時 100 毫秒

你寫的代碼跑起來占記憶體 500M,耗時 1000 毫秒,甚至更多

是以

衡量代碼好壞有兩個非常重要的标準就是:<code>運作時間</code>和<code>占用空間</code>,就是我們後面要說到的時間複雜度和空間複雜度,<code>也是學好算法的重要基石</code>

這也是會算法和不會算法的攻城獅的差別、更是薪資的差別,因為待遇好的大廠面試基本都有算法

可能有人會問:别人是怎麼做到的?代碼沒開發完 運作起來之前怎麼知道占多少記憶體和運作時間呢?

确切的占内用存或運作時間确實算不出來,而且同一段代碼在不同性能的機器上執行的時間也不一樣,可是代碼的基本執行次數,我們是可以算得出來的,這就要說到時間複雜度了

看個栗子

調用這個函數,裡面總執行次數就是3次,這個沒毛病,都不用算

那麼下面這個栗子呢

那這個函數裡面總執行次數呢?根據我們傳進去的值不一樣,執行次數也就不一樣,但是大概次數我們總能知道

這個函數的總執行次數就是 3n + 4 次,對吧

可是我們開發不可能都這樣去數,是以根據代碼執行時間的推導過程就有一個規律,也就是所有代碼執行時間 T(n)和代碼的執行次數 f(n) ,這個是成正比的,而這個規律有一個公式

T(n) = O( f(n) )

完整的公式看着就很麻煩,别着急,這個公式隻要了解一下就可以了,為的就是讓你知道我們表示算法複雜度的 <code>O()</code> 是怎麼來的,我們平時表示算法複雜度主要就是用 <code>O()</code>,讀作大歐表示法,是字母O不是零

隻用一個 <code>O()</code> 表示,這樣看起來立馬就容易了解多了

回到剛才的兩個例子,就是上面的兩個函數

第一個函數執行了3次,用複雜度表示就是 O(3)

第二個函數執行了3n + 4次,複雜度就是 O(3n+4)

這樣有沒有覺得還是很麻煩,因為如果函數邏輯一樣的,隻是執行次數差個幾次,像O(3) 和 O(4),有什麼差别?還要寫成兩種就有點多此一舉了,是以複雜度裡有統一的簡化的表示法,這個執行時間簡化的估算值就是我們最終的<code>時間複雜度</code>

簡化的過程如下

如果隻是常數直接估算為1,O(3) 的時間複雜度就是 <code>O(1)</code>,不是說隻執行了1次,而是對常量級時間複雜度的一種表示法。一般情況下,隻要算法裡沒有循環和遞歸,就算有上萬行代碼,時間複雜度也是O(1)

O(3n+4) 裡常數4對于總執行次數的幾乎沒有影響,直接忽略不計,系數 3 影響也不大,因為3n和n都是一個量級的,是以作為系數的常數3也估算為1或者可以了解為去掉系數,是以 O(3n+4) 的時間複雜度為 <code>O(n)</code>

如果是多項式,隻需要保留n的最高次項,<code>O( 666n³ + 666n² + n )</code>,這個複雜度裡面的最高次項是n的3次方。因為随着n的增大,後面的項的增長遠遠不及n的最高次項大,是以低于這個次項的直接忽略不計,常數也忽略不計,簡化後的<code>時間複雜度為 O(n³)</code>

這裡如果沒有了解的話,暫停了解一下

接下來結合栗子,看一下常見的時間複雜度

上面說了,一般情況下,隻要算法裡沒有循環和遞歸,就算有上萬行代碼,時間複雜度也是 <code>O(1)</code>,因為它的執行次數不會随着任何一個變量的增大而變長,比如下面這樣

上面也介紹了 O(n),總的來說 隻有一層循環或者遞歸等,時間複雜度就是 <code>O(n)</code>,比如下面這樣

比如嵌套循環,如下面這樣的,裡層循環執行 n 次,外層循環也執行 n 次,總執行次數就是 n x n,時間複雜度就是 n 的平方,也就是 <code>O(n²)</code>。假設 n 是 10,那麼裡面的就會列印 10 x 10 = 100 次

還有這樣的,總執行次數為 n + n²,上面說了,如果是多項式,取最高次項,是以這個時間複雜度也是 <code>O(n²)</code>

舉個栗子,這裡有一包糖

如何了解時間複雜度和空間複雜度

這包糖裡有16顆,每天吃這一包糖的一半,請問多少天吃完?

意思就是16不斷除以2,除幾次之後等于1?用代碼表示

循環次數的影響主要來源于 n/2 ,這個時間複雜度就是 <code>O(logn)</code> ,這個複雜度是怎麼來的呢,别着急,繼續看

再比如下面這樣

裡面的列印執行了 4 次,循環次數主要影響來源于 i *= 2 ,這個時間複雜度也是 <code>O(logn)</code>

這個 O(logn) 是怎麼來的,這裡補充一個國小三年級數學的知識點,對數,我們看一張圖

如何了解時間複雜度和空間複雜度

沒有了解的話再看一下,了解一下規律

<code>真數</code>:就是真數,這道題裡就是16

<code>底數</code>:就是值變化的規律,比如每次循環都是i*=2,這個乘以2就是規律。比如1,2,3,4,5...這樣的值的話,底就是1,每個數變化的規律是+1嘛

<code>對數</code>:在這道題裡可以了解成x2乘了多少次,這個次數

仔細觀察規律就會發現這道題裡底數是 2,而我們要求的天數就是這個對數4,在對數裡有一個表達公式

a^b = n  讀作以a為底,b的對數=n,在這道題裡我們知道a和n的值,也就是  2^b = 16 然後求 b

把這個公式轉換一下的寫法如下

log(a) n = b    在這道題裡就是   log(2) 16 = ?  答案就是 4

公式是固定的,這個16不是固定的,是我們傳進去的 n,是以可以了解為這道題就是求 log(2)n = ?

用時間複雜度表示就是 O(log(2)n),由于時間複雜度需要去掉常數和系數,而log的底數跟系數是一樣的,是以也需要去掉,是以最後這個正确的時間複雜度就是 <code>O(logn)</code>

emmmmm.....

沒有了解的話,可以暫停了解一下

其他還有一些時間複雜度,我由快到慢排列了一下,如下表順序

複雜度

名稱

<code>O(1)</code>

常數複雜度

<code>O(logn)</code>

對數複雜度

<code>O(n)</code>

線性時間複雜度

<code>O(nlogn)</code>

線性對數複雜度

<code>O(n²)</code>

平方

<code>O(n³)</code>

立方

<code>O(2^</code><code>n</code><code>)</code>

指數,一點資料量就卡的不行

<code>O(n!)</code>

階乘,就更慢了

這些時間複雜度有什麼差別呢,看張圖

如何了解時間複雜度和空間複雜度

随着資料量或者 n 的增大,時間複雜度也随之增加,也就是執行時間的增加,會越來越慢,越來越卡

總的來說時間複雜度就是執行時間增長的趨勢,那麼空間複雜度就是存儲空間增長的趨勢

空間複雜度就是算法需要多少記憶體,占用了多少空間

常用的空間複雜度有 <code>O(1)</code>、<code>O(n)</code>、<code>O(n²)</code>

隻要不會因為算法裡的執行,導緻額外的空間增長,就算是一萬行,空間複雜度也是 <code>O(1)</code>,比如下面這樣,時間複雜度也是 O(1)

比如下面這樣,n 的數值越大,算法需要配置設定的空間就需要越多,來存儲數組裡的值,是以它的空間複雜度就是 <code>O(n)</code>,時間複雜度也是 O(n)

O(n²) 這種空間複雜度一般出現在比如二維數組,或是矩陣的情況下

不用說,你肯定明白是啥情況啦

就是周遊生成類似這樣格式的

想要學好算法,就必須要了解複雜度這個重要基石

複雜度分析不難,關鍵還是在于多練。每次看到代碼的時候,簡單的一眼就能看出複雜度,難的稍微分析一下也能得出答案。推薦去 leetCode 刷題,App或者PC端都可以。