演算法是為求解乙個問題所需要遵循的、被清楚地指定的簡單指令的集合。對於乙個問題,一旦給定某種演算法並且確定其實正確的,那麼重要的一步就是確定該演算法將需要多少諸如時間或空間等資源量的問題,這就是時間複雜度和空間複雜度存在的意義。常用時間複雜度和空間複雜度來衡量不同演算法的優劣。
一、從數學的角度理解 o(n)、ω(n)、o(n)和θ(n)
通常,我們以函式所處理的資料量來表示演算法的效能,也就是說,對於大小為 n 的資料,我們用函式 f(n) 來表示它的演算法效能。在很多情況下,我們可以完全確定 f 的具體值,但是這通常不是必要的,我們更關心的是當演算法處理的資料量變得無窮大時,演算法的效能將趨近於乙個什麼樣的值,即乙個演算法的增長速率(執行時間、占用空間的增長率)。要描述這種增長率,需要用到一系列新的定義,這些定義的目的是要在函式間建立一種相對的級別,反映出二者之間的相對增長率。
1.1 θ 記法:
θ 記法給出了乙個函式的漸進上界和漸進下界。對於乙個給定的函式 g(n),用 θ(g(n)) 來表示以下函式的集合:
θ(g(n)) =
即,f(n) = θ(g(n)) 表示,對所有的 n>=n0,函式 f(n) 在乙個常量因子內等於 g(n),稱 g(n) 是 f(n) 的乙個漸進緊確界。此外,有乙個定理:對於任意兩個函式 f(n) 和 g(n),我們有 f(n) = θ(g(n)),當且僅當 f(n) = o(g(n)) 且 f(n) = ω(g(n))。
1.2 大 o 記法:
對於乙個給定函式 g(n),用 o(g(n)) 來表示以下函式的集合:
o(g(n)) =
用大 o 記法來給出函式的乙個在常量因子內的漸進上界。即,f(n) = o(g(n)) 表示,函式 f(n) 的增長率小於等於 g(n) 的增長率。
大 o 記法是最常用的一種用來表示演算法增長規律的方法,以大 o 記法來表示函式 f(n) 時,需要注意幾點(其他幾種記法也是):
1)可以忽略 f (n) 的常數項,因為隨著 n 的值變得越來越大,常數項最終變得可忽略不計;
2)可以忽略 f (n) 的常數因子,因為隨著 n 的值越來越大,常數因子也可以忽略不計;
3)只需要考慮 f (n) 的高階項的因子,因為隨著 n 的值越來越大,高階因子的值會迅速超過低階因子的值。
1.3 大 ω 記法:
大 ω 記法給出了乙個函式的漸進下界。對於給定的函式 g(n),用 ω(g(n)) 表示以下函式的集合:
ω(g(n)) =
即,f(n) = ω(g(n)) 表示,函式 f(n) 的增長率小於等於 g(n) 的增長率。
1.4 小 o 記法:
大 o 記法提供的漸進上界可能是漸進緊確的也可能不是,因此,我們使用小 o 記號來表示乙個非漸進緊確的上界。因此, o(g(n)) 表示這樣的集合:
o(g(n)) =
即,f(n) = o(g(n)) 表示,函式 f(n) 的增長率小於 g(n) 的增長率。
二、時間複雜度
時間複雜度指的是執行乙個演算法所需要的時間。這不一定是乙個確切的時間,通常,我們需要知道的是乙個演算法在最壞情況下執行(比如輸入規模無限大)所需要的時間,也就是尋找演算法執行時間的乙個漸進上界來作為演算法的時間複雜度,通過比較多個演算法的這個上界,可以知道哪個演算法執行比較快,哪個比較慢。這裡就用到了上面所講的大 o 記法,我們通常使用大 o 記法來表示乙個演算法的時間複雜度。
2.1 計算時間複雜度的方法
乙個程式執行的總時間主要與以下兩點有關:
1)執行每條語句所耗的時間;
2)執行每條語句的頻率或者說次數。
其中,第一條取決於計算機、編譯器和作業系統,第二條取決於程式本身和輸入規模。如果對於乙個程式的所有部分,我們都知道了這些性質,則將它們相乘並將所有指令的成本相加即可得到總執行時間,這是乙個確切的時間。用大 o 記法對這個確切的時間表示式( f(n) )進行處理:
1)執行每條語句所耗的時間為常數,常數項忽略,因此只需要考慮每條語句的執行頻率;
2)忽略執行頻率函式的常數因子;
3)只保留執行頻率函式的最高端項。
即可得到乙個大 o 表示式,這個大 o 表示式就是該程式的時間複雜度。舉乙個例子:
1int sum(intn)2
對這段**進行分析:首先,宣告不計入時間;第 5 行和第 8 行各佔乙個時間單元;第 7 行每執行一次占用四個時間單元(兩次乘法、一次加法、一次賦值),執行 n 次,共占用 4n 個時間單元;第 6 行在初始化 i、測試 i < n 和對 i 進行自增操作中隱含著開銷,所有這些開銷總共占用 2n+2 個時間單元(i 初始化占用乙個時間單元、測試 i < n 要執行 n+1 次,占用 n+1 個時間單元、自增操作執行 n 次,占用 n 個時間單元)。因此,上述**所耗的時間總量為 2 + 4n +(2n+2) = 6n + 4。根據前面所提到的方法,忽略常數項、忽略常數因子,可以得到這段**的時間複雜度為 o(n)。
當然,如果每次都要像這樣逐行地對**進行分析來計算時間複雜度顯然是不可行的,通常,許多**的大 o 表示式是已知的,直接根據已知的大 o 結果即可得到**最後的結果。因此,關於上述**的時間複雜度,還可以這樣分析: for 迴圈是 o(n) 語句,其他語句的執行時間為常數項,忽略掉,即可得到整段**的時間複雜度為 o(n).
2.2 一些常見的時間複雜度
大o表示式描述例
o(1)
常數級,表明演算法的執行時間不隨問題規模 n 的增大而增大;
另外,對於常數 c,有 o(c) = o(1)
普通語句,如 a = b+c
o(logn)
對數級,表明演算法的執行時間隨問題規模 n 的增大而呈對數增長;
對數的底數與增長的數量級無關(不同的底數相當於常數因子),
因此在說明對數級時一般使用 logn 來表示。
二分查詢
o(n)
線性級,表明演算法的執行時間隨問題規模 n 的增大而呈線性增長
單個for迴圈
o(nlogn)
線性對數級,表明演算法的執行時間與問題規模 n 的關係為 nlogn
歸併排序、快速排序
o(n^2)
平方級,表明演算法的執行時間隨問題規模 n 的增大而呈平方級增長
二層 for 迴圈、選擇排序
o(n^3)
立方級,表明演算法的執行時間隨問題規模 n 的增大而呈立方級增長
三層 for 迴圈
o(2^n)
指數級,表明演算法的執行時間隨問題規模 n 的增大而呈指數增長
窮舉查詢
按照所消耗時間從小到大排序:o(1) < o(logn) < o(n) < o(nlogn) < o(n^2) < o(n^3) < o(2^n).
三、空間複雜度
演算法的空間複雜度用來描述演算法在執行時臨時占用儲存空間的大小,記作 s(n) = o(f(n)) ,表示演算法所占用的儲存空間與問題規模 n 的關係,其分析計算方式也與時間複雜度類似。
對於乙個演算法,其時間複雜度和空間複雜度往往是相互影響的。當追求乙個較好的時間複雜度時,可能會使得空間複雜度的效能變差,即占用更多的儲存空間;相反,當追求更好的空間複雜度時,可能會使得時間複雜度變差,消耗更多的執行時間。在設計乙個程式(尤其是大型程式)時,需要綜合考慮演算法的各項效能,以在二者之間尋求乙個平衡點,達到最大收益。
時間複雜度 空間複雜度 演算法基礎
1空間複雜度 演算法的空間效能分析 1 演算法的空間效能的影響因素 指令空間 由機器決定 資料空間 常量,變數占用空間 環境棧空間 2 度量方法 單個常量,變數 由機器和編譯器規定的型別儲存決定。陣列變數 所佔空間等於陣列大小乘以單個陣列元素所佔的空間。結構變數 所佔空間等於各個成員所佔空間的累加 ...
演算法時間複雜度和空間複雜度簡介
一 資料結構 資料結構是一門研究非數值計算的程式設計問題中的操作物件,以及它們之間的關係和操作等相關問題的學科。邏輯結構 是指資料物件中資料元素之間的相互關係,也是我們今後最需要關注和討論的問題。物理結構 是指資料的邏輯結構在計算機中的儲存形式。鏈式儲存結構 是把資料元素存放在任意的儲存單元裡,這組...
演算法中時間複雜度和空間複雜度簡介
作為程式設計師,大家應該都聽過這樣乙個公式 程式設計 演算法 資料結構,我們所編寫的程式都可以使用這個公式來概括,其中演算法主要是針對世間和空間兩個維度進行考核。1.1 計算表示式 要計算演算法的時間消耗,可以通過加日誌列印,獲取該程式執行算消耗的時間,但在實際的使用中,我們不可能針對每一段程式都去...