博客:blog.shinelee.me | 博客園 | CSDN
時間復雜度
如何評估一個算法的計算時間?
一個算法的實際運行時間很難評估,當時的輸入、CPU主頻、內存、數據傳輸速度、是否有其他程序在搶占資源等等,這些因素都會影響算法的實際運行時間。為了公平地對比不同算法的效率,需要脫離開這些物理條件,抽象出一個數學描述。在所有這些因素中,問題的規模往往是決定算法時間的最主要因素。因此,定義算法的時間復雜度\(T(n)\),用來描述算法的執行時間隨着輸入規模的增長將如何變化,增長速度是怎樣的。
在輸入規模較小時,運行時間本來就少,不同算法的差異不大。所以,時間復雜度通常關注的是輸入規模\(n\)較大時運行時間的變化趨勢,稱之為漸進復雜度,采用大O記號,表示漸進上界,對於任意的\(n >> 2\),若有常數\(c\)和函數\(f(n)\)滿足
則記作
可以簡單地認為,\(O(f(n))\)表示運行時間與\(f(n)\)成正比,比如\(O(n^2)\)表示運行時間與輸入規模的平方成正比,這樣講雖然並不嚴謹,但一般情況下無傷大雅。
在\(n\)很大時,常數\(c\)變得無關緊要,不同算法間的比較主要關注\(f(n)\)部分的大小。比如,在\(n >> 100\)時,\(n^2\)要比\(100n\)大得多,因此重點關注\(n^2\)和\(n\)增長速度的差異即可。
不同時間復雜度的增長速度對比如下,圖片來自Big-O Cheat Sheet Poster,
除了大\(O\)記號,還有大\(\Omega\)記號和\(\Theta\)記號,分別表示下界和確界,
他們的關系如下圖所示,圖片截自鄧俊輝-數據結構C++描述第三版
常用數據結構操作與算法的復雜度
下面匯總摘錄了常用數據結構操作和排序算法的復雜度,來源見引用。其中包含最壞時間復雜度、平均時間復雜度以及空間復雜度等,對於排序算法還含有最好時間復雜度。
附帶上鏈接:
Array Stack Queue Singly-Linked List Doubly-Linked List Skip List Hash Table Binary Search Tree Cartesian Tree B-Tree Red-Black Tree Splay Tree AVL Tree KD Tree
Quicksort Mergesort Timsort Heapsort Bubble Sort Insertion Sort Selection Sort Tree Sort Shell Sort Bucket Sort Radix Sort Counting Sort Cubesort
以及 Data Structures in geeksforgeeks。
輸入規模較小時的情況
漸進復雜度分析的是輸入規模較大時的情況,輸入規模較小時呢?
在輸入規模較小時,就不能輕易地忽略掉常數\(c\)的作用,如下圖所示,圖片來自Growth Rates Review。復雜度增長快的在輸入規模較小時可能會小於復雜度增長慢的。
所以在選擇算法時,不能無腦上看起來更快的高級數據結構和算法,還得具體問題具體分析,因為高級數據結構和算法在實現時往往附帶額外的計算開銷,如果其帶來的增益無法抵消掉隱含的代價,可能就會得不償失。
這同時也給了我們在代碼優化方向上的啟示,
- 一是從\(f(n)\)上進行優化,比如使用更高級的算法和數據結構;
- 還有是對常數\(c\)進行優化,比如移除循環體中不必要的索引計算、重復計算等。
以上