【數據結構與算法】分析時間複雜度與空間複雜度

本文是覃超老師的《算法訓練營》的學習筆記,此筆記的內容包含了學習後的個人記錄、個人總結、理解和思想。從而更好的學習算法。

前言

學習任何一門知識的時候,我們需要分析清楚這門知識的核心是什麼,從而在這個核心中我們可以得到什麼。如果我們是盲目的吸收知識,其實很多知識我們都是在目前場景、工作、生活中無法使用的。也是因爲學習之後無法運用,所以我們很快就會遺忘,或者是在學習的過程中很容易就會放棄。

在一生的學習的過程中,發現學習我們急需使用或者能給我們及時帶來價值的知識,我們會學的更加牢固,更加能堅持學習。

學習《數據結構與算法》這門知識的核心是什麼?又能得到什麼呢?

  1. 弄懂編程的底層邏輯;
  2. 在編程的過程中,擁有一個哆啦A夢一樣百寶工具袋;
  3. 在遇到性能問題的時候,有算法的思維邏輯和規則來解決問題;
  4. 提高編程思維;

這篇筆記記錄了算法的核心時間和空間複雜度,《數據結構與算法》都是圍繞着這個核心開展的。它的存在也是爲了解決我們在編程的過程中性能問題,同時也讓我們有更高級的思維和思路,寫出更優質的程序。

複雜度指標 Big O Notation

  • O (1): 常數複雜度 - Constant Complexity
  • O (log n): 對數複雜度 - Logarithmic Complexity
  • O (n): 線性複雜度 - Linear Complexity
  • O (n^2): 平方複雜度 - N square Complexity
  • O (2^n): 指數 - Exponential Growth
  • O (n!): 階乘 - Factorial

如何看時間複雜度

  • 分析函數;
  • 根據n的不同情況會運行多少次;
  • 最後得出一個平均的運行次數的量級;

Complexity 例子

O (1) - 常數複雜度

let n = 1000;
console.log("Hello - your input is: " + n)

O (N) - 線性複雜度

for (let i = 1; i <= n; i++) {
  console.log("Hello world - your input is: " + i)
}

O (N^2)

for (let i = 1; i <= n; i++) {
  for (let j = 1; j <= n; j++) {
  	console.log("Hello world - your input is: " + i + " and " + j)
  }
}

那如果我們不是嵌套兩層for循環,是把兩個循環分開來存放呢?這種方式時間複雜度是?

for (let i = 1; i <= n; i++) {
  console.log("Hello world - your i input is: " + i)
}

for (let j = 1; j <= n; j++) {
  console.log("Hello world - your j input is: " + j)
}

很多小夥伴應該猜到了,就是2* n次的複雜度,那就是O(2n)。其實還是O(n)的時間複雜度。

O(log(n))

for (let i = 1; i < n; i = i * 2) {
  console.log("Hello world - your input is: " + i);
}

O(k^n)

// Fibonacci遞歸
function fib (n) {
  if (n <= 2) return n;
  return fib(n-1) + fib(n-2);
}

時間複雜度曲線

  • y軸是Operations就是操作複雜度的指數;
  • x軸是Elements就是n我們的循環次數 ;
  • 這裏我們可以看到在n比較小的時候,複雜度是相對穩定的;
  • 但是當n越來越大時,Big-O複雜度就會急速飆升;

所以在我們寫程序的時候,如果能把時間和空間複雜度從O(n^2)降到O(n)或者O(1)後,我們得到的優化收益是非常高的!

  • 在編寫程序的時候一定要注意到它的時間和空間複雜度,這樣編寫的時候就能預測出這段代碼的性能級別;
  • 用最簡潔的時間和空間複雜度完成這段程序;
  • 這樣就是最頂尖的職業編程選手了;
  • 因爲複雜度越高,程序損耗的時間(處理時間)和資源(內存)就越大;

降低時間和空間複雜度

我們用個例子就可以看到如何在編程中降低複雜度:

計算:1 + 2 + 3 + … + n

方法一: 循環1到n然後累加 (時間複雜度 O(n))

let sum = 0
for (let i = 1; i < n; i++) {
  sum += i
}
console.log(sum)

方法二: 求和公式 sum = n(n+1)/2 (時間複雜度 O(1))

let sum = n * (n + 1) / 2
console.log(sum)

注意:

  1. 在做題或者面試的時候先確認題目,確保一切的條件和題目的理解無誤;
  2. 想出所有可能的解決方案;
  3. 同時比較每個方法的時間和空間複雜度;
  4. 接下來找出最優的解決方案(時間最快,內存使用最少)

判斷時間和空間複雜度

斐波那契(Fibonacci)例子

公式:F(n) = F(n - 1) + F(n - 2)

我們可以直接使用遞歸來解題:

function fib(n) {
  if (n <= 2) return n
  return fib(n - 1) + fib(n - 2)
}
  • 這個fib斐波那契函數中是一個遞歸
  • 每一次傳入一個n值時,都會循環遞歸fib方法來一層一層往下計算;
  • 最後到達n小於2,返回最後的n值;

那針對這個遞歸,我們怎麼計算它的時間複雜度呢?

  • 要推斷出這個程序的複雜度,首先我們要知道具體在這個函數中程序做了什麼;
  • 我們距離現在傳入n6,那就是運行fib(6)
  • 這個時候6被傳入這個方法,然後返回的就是fib(5)+fib(4),這時fib(5)fib(4)就會再進入fib函數,這裏就分開了兩個分支了。以此類推我們就會出現以下一個樹狀過程:

  • 通過上圖展開來的樹,我們可以看到每一層是上一層的2倍:fib(6)展開爲fib(5)+fib(4),然後fib(5)fib(4)又展開了兩個。
  • 所以fibonacci的執行次數就是一個指數級 - O(2^n)
  • 這裏我們也可以看到fib(3)fib(4)等等,都被重複計算了多次,所以這個計算的複雜度高達2的6次方
  • 所以在做題和麪試的時候就不要運用上面的代碼實例,我們要加入緩存機制,緩存重複計算的結果或者用一個循環來寫,從而降低這個程序的複雜度。

主定理 Master Theorem

任何一個分治或者遞歸函數都可以通過這個定理來算出它們的時間複雜度。這個定理裏面有4種最常用的,只要記住這4種就可以了。

算法 (Algorithm) 時間複雜度 (Run time)
二分查找 (Binary search) O(log n)
二叉樹遍歷 (Binary tree traversal) O(n)
排序二維矩陣 (Optimal sorted matrix search) O(n)
歸併排序 (Merge sort) O(n log n)

常見面試題

  • 二叉樹遍歷中的前序、中序、後序:時間複雜度是多少?
    • 時間複雜度是 O(n),無論是前序、中序或者後序每一個節點都會訪問一次,並且僅訪問一次;
    • 所以就是二叉樹的節點總數,也就是O(n)的線性時間複雜度;
  • 圖的遍歷:時間複雜度是多少?
    • 時間複雜也是O(n), 這裏的n就是圖裏面的節點總數;
  • 搜索算法:DFS、BFS時間複雜度是多少?
    • DFS是深度優先,BFS是廣度優先算法。
    • 不管是深度優先還是廣度優先,因爲訪問的節點只訪問一次,所以時間複雜度也是O(n)的。(n指的是搜索空間裏面的節點總數)
  • 二分查找:時間複雜度是多少?
    • 答案是O(log n)

總結

  • 程序複雜度:Big O Notation
    • O (1)O(log n)O(n)O(n^2), … 等等,越複雜程序性能越差;
    • 分析複雜度法則:分析代碼的邏輯,找到程序中運行的次數;
    • 降低程序時間和空間複雜度可以提升代碼的質量,同時優化程序的性能;
  • 主定理:
    • 所有的分治或者遞歸函數都可以通過主定理來分析出它的時間複雜度
  • 常見面試題:
    • 二叉樹遍歷中的前序、中序、後序:時間複雜度是多少? - O(n)
    • 圖的遍歷:時間複雜度是多少? - O(n)
    • 搜索算法:DFS、BFS時間複雜度是多少? - O(n)
    • 二分查找:時間複雜度是多少? - O(log n)

我是三鑽,一個在技術銀河中等你們一起來終身漂泊學習。
點贊是力量,關注是認可,評論是關愛!下期再見 👋!

公衆號《技術銀河》回覆"算法資料",可以獲得這個系列文章的PDF版其他資料

推薦專欄

小夥伴們可以查看或者訂閱相關的專欄,從而集中閱讀相關知識的文章哦。

  • 📖 《據結構與算法》 — 到了如今,如果想成爲一個高級開發工程師或者進入大廠,不論崗位是前端、後端還是AI,算法都是重中之重。也無論我們需要進入的公司的崗位是否最後是做算法工程師,前提面試就需要考算法。

  • 📖 《FCC前端集訓營》 — 根據FreeCodeCamp的學習課程,一起深入淺出學習前端。穩固前端知識,一起在FreeCodeCamp獲得證書

  • 📖 《前端星球》 — 以實戰爲線索,深入淺出前端多維度的知識點。內含有多方面的前端知識文章,帶領不懂前端的童鞋一起學習前端,在前端開發路上童鞋一起燃起心中那團火🔥

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章