前言算法
這是 2019 年 1 月 20 日的一篇文章,之因此今天從新發一遍,由於我以爲算法的複雜度分析是入門算法的一個基本功。有句話說的好,「練武不練功,到老一場空」。我也是正好溫習了一遍,若是不常常用,我也有遺忘的時候。數組
另一點就是,以往看似很少的一篇動畫技術文章,它的寫做和製做很是耗時間的,這個時間會超出你的想象。我我的初心是不想匆匆撩草的完成一篇文章,而後發到公衆號,這對我和你來講是不負責任的。數據結構
因此我仍是選擇留出多一點時間好好去寫,而後把圖設計的更易懂些,不求一天一更,只求每篇文章拿出我最好的狀態來分享。ide
一、數據結構是用來幹嗎的?性能
數據結構與算法的誕生是讓計算機「執行的更快」、「更省空間」的。動畫
二、用什麼來評判數據結構與算法的好壞?設計
從「執行時間」和「佔用空間」兩個方面來評判數據結構與算法的好壞。3d
三、什麼是複雜度?code
用「時間複雜度」和「空間複雜度」來描述性能問題,二者統稱爲複雜度。對象
四、複雜度描述了什麼?
複雜度描述的是算法執行時間(或佔用空間)與數據規模的增加關係。
一、和性能分析相比有什麼優勢?
輔助度分析有不依賴執行環境、成本低、效率高、易操做、指導性強的特色。
二、爲何要複雜度分析?
複雜度描述的是算法執行時間(或佔用空間)與數據規模的增加關係。
一、什麼方法能夠進行復雜度分析?
方法:「大 O 表示法」
二、什麼是大 O 表示法?
算法的「執行時間」與每行代碼的「執行次數」成正比【T(n) = O(f(n)) 】=》其中T(n)表示算法執行總時間,f(n)表示每行代碼執行總次數,而n每每表示數據的規模。
三、大 O 表示法的特色?
因爲時間複雜度描述的是算法執行時間與數據規模的增加變化趨勢,常量階、低階以及係數實際上對這種增加趨勢不產決定性影響,因此在作時間複雜度分析時忽略這些項。
四、複雜度分析法則
[單段代碼看頻率]:看代碼片斷中「循環代碼」的時間複雜度。
[多段代碼看最大]:若是多個 for 循環,看「嵌套循環最多」的那段代碼的時間複雜度。
[嵌套代碼求乘積]:循環、遞歸代碼,將內外嵌套代碼求乘積去時間複雜度。
------------------❤------------------
時間複雜度
一、什麼是複雜度?
全部代碼的「執行時間 T(n)」 與每行代碼的「執行次數n」 成正比【T(n) = O(f(n)) 】。
二、分析的三個方法
■ 最多法則
忽略掉公式中的常量、低階、係數,取最大循環次數就能夠了,也就是循環次數最多的那行代碼。
Example
1// 求n個數字之和 2int xiaolu(int n) { 3 int sum = 0; 4 for (int i = 1; i <= n; ++i) { 5 sum = sum + i; 6 } 7 return sum; 8 }
分析
第二行是一行代碼,也就是常量級別,與 n 沒有關係,能夠忽略,4、五行代碼是咱們重點分析對象,與 n 有關,時間複雜度就是反映執行時間和 n 數據規模的關係。求 n 個數據之和須要執行 n 次。因此時間複雜度爲 O(n)。
■ 加法法則
總複雜度等於循環次數最多的那段複雜度。
Example
1int xiaolu(int n) { 2 int sum = 0; 3 //循環一 4 for (int i = 1; i <= 100; j++) { 5 sum = sum + i; 6 } 7 //循環二 8 for (int j = 1; j <= n; j++) { 9 sum = sum + i; 10 } 11 }
分析
上邊有兩個循環,一個循環 100 次,另外一個循環 n 次,咱們選擇循環次數最多的那一個且和「數據規模 n 」相關的循環。由上可知,咱們很容易選出循環二,即和數據規模 n 有關,循環次數最多,循環次數最多的那段代碼時間複雜度就表明整體的時間複雜度,爲 O(n) ;
■ 乘法法則
當咱們遇到嵌套的 for 循環的時候,怎麼計算時間複雜度呢?那就是內外循環的乘積。
Example
1 for (int j = 1; j <= n; j++) { 2 for(int i = 1; i <= n; i++) 3 sum = sum + i; 4 }
分析
外循環一次,內就循環 n 次,那麼外循環 n 次,內就循環 n*n 次。因此時間複雜爲 O(n²)。
空間複雜度
一、什麼是空間複雜度?
表示算法的「存儲空間」與「數據規模」之間的增加關係
Example
1int xiaolu(int n) { 2 int sum = 0; 3 //循環一 4 for (int i = 1; i <= 100; j++) { 5 sum = sum + i; 6 } 7 //循環二 8 for (int j = 1; j <= n; j++) { 9 sum = sum + i; 10 } 11 }
分析
在全部代碼中,咱們很容易尋找到存儲空間相關的代碼,就是第二行,申請了一個 常量級別 大小的存儲空間,因此空間複雜度爲 O(1)。
二、最多見的空間複雜度
O(1)、O(n)、O(n²)。
■ O(1)
常量級的時間複雜度表示方法,不管是一行代碼,仍是多行,只要是常量級的就用 O(1) 表示。
Example
1int i = 1; 2int j = 2; 3int sum = i + j;
分析
由於這三行代碼,也就是常量級別的代碼不隨 n 數據規模的改變而改變。(循環、遞歸除外)
■ O(logn) | O(nlogn)
「對數階時間複雜度」,最難分析的一種時間複雜度。
Example
1 i=1; 2 while (i <= n) { 3 i = i * 3; 4 }
分析
要求這段代碼的時間複雜度就求這段代碼執行了多少次,看下圖具體分析。
補充
不論是以 2 爲底、以 3 爲底,仍是以 10 爲底,能夠把全部對數階的時間複雜度都記爲 O(logn),由於對數之間能夠轉換的,參照高中課本。
*■ O(m+n) | O(mn)**
參照上邊講到的加法和乘法法則。
一、最好、最壞時間複雜度
所謂的最好、最壞時間複雜度分別對應代碼最好的狀況和最壞的狀況下的執行。
Example
1 //在一個 array 數組中查找一個數據 a 是否存在 2for (int i = 1; i < n; i++) { 3 if (array[i] == a) { 4 return i; 5 } 6 }
分析:
一、最好狀況就是數組的第一個就是咱們要查找的數據,上邊代碼之執行一遍就能夠,這種狀況下的時間複雜度爲最好時間複雜度,爲 O(1)。
二、最壞的狀況就是數組的最後一個纔是咱們要查找的數據,須要循環遍歷 n 遍數組,也就對應最壞的時間複雜度爲 O(n) 。
二、平均時間複雜度
平均時間複雜度須要藉助機率論的知識去分析,也就是咱們機率論中所說的加權平均值,也叫作指望值。
分析
好比上方的例子,假設咱們查找的數據在數組中的機率爲 1/2;出如今數組中的機率爲 n/1,根據下邊的公式就能夠算出出現的機率爲 1/2n 。
而後咱們再把每種狀況考慮進去,就能夠計算出平均時間複雜度。
三、均攤時間複雜度
■什麼是均攤時間複雜度?
好比咱們每 n 次插入數據的時間複雜度爲 O(1),就會有一次插入數據的時間複雜度爲 O(n),咱們將這一次的時間複雜度平均到 n 次插入數據上,時間複雜度仍是 O(1)。
■ 攤還分析
好比咱們每 n 次插入數據的時間複雜度爲 O(1),就會有一次插入數據的時間複雜度爲 O(n),咱們將這一次的時間複雜度平均到 n 次插入數據上,時間複雜度仍是 O(1)。
■ 適用場景
通常應用於某一數據結構,連續操做時間複雜度比較低,可是個別狀況時間複雜度特別高,咱們將特別高的這一次進行均攤到較低的操做上。
■ 幾種複雜度性能對比