算法分析初步
註:這個是我去年這個時候做的筆記,現在這部分內容我已經基本掌握,在博客上做個備份
算法分析
一、漸進符號
漸近分析是一種描述函數在極限附近的行為的方法,算法分析一般考慮給定算法在輸入非常大的數據集時候的性能。
定義1.1:\(O\)記號
\(f(n)=O(g(n)):\)表示存在正常量\(c\)和\(n_0\),使得對所有的\(n\ge n_0\),有\(0\le f(n)\le cg(n)\)。
這個符號用來表示函數的漸進上界,比如\(2n^2=O(n^3)\)。
這其中的等號不是對稱的,因為\(O\)記號定義了一個函數集:
\(O(g(n))=\{f(n):\text{存在正常量和$n_0$,使得對所有的$n\ge n_0,0\le f(n)\le cg(n)$}\}\)
所以不能夠理解為\(2n^2\)等於\(O(n^3)\),而只能理解為\(2n^2\)屬於函數集\(O(n^3)\)
這個符號還可以用來描述誤差界限,如:\(f(n)=n^3+O(n^2)\),表示這個函數主要是\(n^3\),還有一些至多\(O(n^2)\)的項。
類似地我們可以定義一個函數的漸進下界:
定義1.2:\(\Omega\)記號
\(f(n)=\Omega(g(n)):\) 表示存在正常量\(c\)和\(n_0\),使得對所有的\(n\ge n_0\),有\(0\le cg(n)\le f(n)\)。
\(\Omega(g(n))=\{f(n):\text{存在正常量$c$和$n_0$,使得對所有的$n\ge n_0,有0\le cg(n)\le f(n)$}\}\)
如:\(\sqrt{n}=\Omega(\log n)\)。
接下來我們定義漸進緊確界:
定義1.3:\(\Theta\)記號
\(\Theta(g(n))=\{f(n): \text{存在正常量$c_1$,$c_2$和$n_0$,使得對所有的$n\ge n_0,有0\le c_1g(n)\le f(n)\le c_2g(n)$}\}\)
如:\(2n^2=\Theta(n^2)\)
顯然對任意兩個函數\(f(n)\)和\(g(n)\),我們有\(f(n)=\Theta(g(n))\),當且僅當\(f(n)=O(g(n))\)且\(f(n)=\Omega(g(n))\)
由\(O\)定義的漸進上界可能是也可能不是漸進緊確的,用類似\(ε-δ\)
定義1.4:\(o\)記號
\(o(g(n))=\{f(n):\text{對任意常量$c>0$,存在常量$n_0>0$,使得對所有的$n\ge n_0,有0\le f(n)\le cg(n)$\}}\)
如:\(n^2=o(n^3)(n_0=\frac{2}{c})\)。
有些學者還喜歡用\[\lim_{n\to\infty} \frac{f(n)}{g(n)}=0\]來定義\(o\)符號,帶入函數極限的定義,對任給正數\(n\),總存在\(n_0\),使得當\(n>n_0\)時有\(\left|\frac{f(n)}{g(n)}-0\right|<c\),整理即得\(f(n)<cg(n)\)
同樣地我們定義\(\Omega\)記號
定義1.5:\(\Omega\)記號
\(\Omega(g(n))=\{f(n):\text{對任意常量$c>0$,存在常量$n_0>0$,使得對所有的$n\ge n_0$,有$0\le cg(n)\le f(n)$}\}\)
以及\[\lim_{n\to\infty}\frac{f(n)}{g(n)}=+\infty\]
性質
- 傳遞性:\(5\)個符號都具有傳遞性,\(f(n)=O(g(n))\)且\(g(n)=O(h(n)) \to f(n)=O(h(n))\)
- 自反性:\(\Omega,\Theta,O\)記號具有自反性,\(f(n)=O(f(n))\)
- 對稱性:\(\Theta\)具有對稱性,\(f(n)=\Theta(g(n))\),當且僅當\(g(n)=\Theta(f(n))\)
- 轉置對稱性:\(f(n)=O(g(n))\)當且僅當\(g(n)=\Omega(f(n))\),\(f(n)=o(g(n))\)當且僅當\(g(n)=\Omega(f(n))\)
二、遞歸式的求解
我們經常使用遞歸方法來解決問題,因此會出現一些遞歸式,對於遞歸式時間復雜度的求解,並沒有通用的方法,主要有以下幾種方法
1.代入法,先猜後證
先猜出一個結果,再使用數學歸納法進行證明
註意,在進行數學歸納法的推導時,不要使用\(O\)記號進行推導,很容易產生悖論
比如,我們可以證明\(n=O(1)\),進而所有的算法都是\(O(1)\)的
歸納奠基:\(1=O(1)\)
歸納假設:假設\(k=O(1)\)
歸納遞推:\(k+1=O(1)+O(1)=O(1)\)
所以對所有的自然數\(n\)均成立,\(n=O(1)\)
錯誤在於,在向下遞推\(O(1)\)的過程中,\(O(1)\)會依賴於\(n\)變化
解:\(T(n)=4T(n/2)+n,T(1)=\Theta(1)\)
首先猜想它是\(O(n^2)\)的,使用第二數學歸納法,假設存在$c_1,c_2,s.t. \(當\)k
同理我們還可以證明這個遞推式是\(\Omega(n^2)\)的,進而推出這個遞推式是\(\Theta(n^2)的\)
2.遞歸樹方法
畫出遞歸樹,對遞歸樹的每一層進行求和,然後將所有的層求和
如對於\(T(n)=3T\left( \left \lfloor \frac{n}{4} \right\rfloor\right)+\Theta(n^2)\),畫出的遞歸樹如下
對表達式進行求和,可得\[T(n)=\sum_{1}^{\log_4n}{\left(\frac{3}{16}\right)^icn^2}+\Theta(n^{\log_43})<\sum_{1}^{\infty}{\left(\frac{3}{16}\right)^icn^2}+\Theta\left(n^{\log_43}\right)=\frac{16}{13}cn^2+\Theta\left(n^{\log_43}\right)=O(n^2)\]
因為表達式中有\(\Theta(n^2)\),所以\(T(n)\)顯然是\(\Omega(n^2)\)的,所以\(T(n)\)是\(\Theta(n^2)\)的
很多時候我們不必求出準確值,而只需進行恰當的放縮,如\(T(n)=T\left(\frac{n}{3}\right)+T\left(\frac{2n}{3}\right)+cn\),顯然兩側下降的不一樣快,左子樹會先到達葉子節點,但我們使用\(\log_{\frac{3}{2}}n\)作為樹高和計算葉子節點的數量,不會改變\(O\)的數量級
3.主方法
套現成的公式
主定理:令\(a\ge 1\)和\(b>1\)是常數,\(f(n)\)是一個函數,\(T(n)\)是定義在非負整數上的遞歸式\(T(n)=aT(\frac{n}{b})+f(n)\)
那麽\(T(n)\)有如下漸進界:
\(1\).若對於某個常數\(ε>0\)有\(f(n)=O\left(n^{\log_ba-ε}\right)\),則\(T(n)=\Theta\left(n^{\log_ba}\right)\)
\(2\).若對於某個常數\(k\ge 0\)有\(f(n)=\Theta\left(n^{\log_ba}\log^kn\right)\),則\(T(n)=\Theta\left(n^{\log_ba}\log^{k+1}n\right)\)
\(3\).若對某個常數\(ε>0\)有\(f(n)=\Omega\left(n^{\log_ba+ε}\right)\),且對某個常數\(c<1\)和所有足夠大的\(n\)有\(af\left(\frac{n}{b}\right)\le cf(n)\),則\(T(n)=\Theta(f(n))\)
4.Akra-Bazzi方法
\[ T(x)=\left\{ \begin{aligned} \Theta(1)\qquad若1\le x \le x_0 \\sum_{i=1}^k{a_iT(b_ix)}+f(x)\qquad若x>x_0 \\end{aligned} \right. \]
則遞歸式的解為
\[
T(n)=\Theta\left(x^p\left(1+\int_1^x\frac{f(u)}{u^{p+1}}du\right)\right)
\]
證明均見《算法導論》
三、常用算法的時間復雜度
搜索
算法 | 數據結構 | 時間復雜度(平均) | 時間復雜度(最差) | 空間復雜度(最差) |
---|---|---|---|---|
深度優先搜索(DFS) | \(\left\|V\right\|\)個點\(\left\|E\right\|\)個邊的圖 | - | \(O(\left\|E\right\|+\left\|V\right\|)\) | \(O(\left\|V\right\|)\) |
廣度優先搜索(BFS) | \(\left\|V\right\|\)個點\(\left\|E\right\|\)個邊的圖 | - | \(O(\left\|E\right\|+\left\|V\right\|)\) | \(O(\left\|V\right\|)\) |
二分查找 | 排好序的數組 | \(\Theta(\log n)\) | \(O(\log n)\) | \(O(1)\) |
窮舉查找 | 數組 | \(\Theta(n)\) | \(O(n)\) | \(O(1)\) |
Dijkstra算法(小根堆) | \(\left\|V\right\|\)個點\(\left\|E\right\|\)個邊的圖 | \(\Theta((\left\|V\right\|+\left\|E\right\|)\log\left\|V\right\|)\) | \(O((\left\|V\right\|+\left\|E\right\|)\log\left\|V\right\|)\) | \(O(\left\|V\right\|)\) |
Dijkstra算法(無序數組) | \(\left\|V\right\|\)個點\(\left\|E\right\|\)個邊的圖 | $ \Theta(\left|V\right|^2)$ | \(O(\left\|V\right\|^2)\) | \(O(\left\|V\right\|)\) |
Bellman-Ford算法 | \(\left\|V\right\|\)個點\(\left\|E\right\|\)個邊的圖 | \(\Theta(\left\|V\right\|\left\|E\right\|)\) | \(O(\left\|V\right\|\left\|E\right\|)\) | \(O(\left\|V\right\|)\) |
Floyd-Warshell算法 | \(\left\|V\right\|\)個點\(\left\|E\right\|\)個邊的圖 | \(\Theta(\left\|V\right\|^3)\) | \(O(\left\|V\right\|^3)\) | \(O(\left\|V\right\|^2)\) |
排序
算法 | 數據結構 | 時間復雜度(最佳) | 時間復雜度(平均) | 時間復雜度(最差) | 輔助空間復雜度(最差) |
---|---|---|---|---|---|
快速排序 | 數組 | \(\Omega(n\log n)\) | \(\Theta(n\log n)\) | \(O(n^2)\) | \(O(n)\) |
歸並排序 | 數組 | \(\Omega(n\log n)\) | \(\Theta(n\log n)\) | \(O(n\log n)\) | \(O(n)\) |
堆排序 | 數組 | \(\Omega(n\log n)\) | \(\Theta(n\log n)\) | \(O(n\log n)\) | \(O(1)\) |
冒泡排序 | 數組 | \(\Omega(n)\) | \(\Theta(n^2)\) | \(O(n^2)\) | \(O(1)\) |
插入排序 | 數組 | \(\Omega(n)\) | \(\Theta(n^2)\) | \(O(n^2)\) | \(O(1)\) |
選擇排序 | 數組 | \(\Omega(n^2)\) | \(\Theta(n^2)\) | \(O(n^2)\) | \(O(1)\) |
桶排序 | 數組 | \(\Omega(n+k)\) | \(\Theta(n+k)\) | \(O(n^2)\) | \(O(nk)\) |
基數排序 | 數組 | \(\Omega(nk)\) | \(\Theta(nk)\) | \(O(nk)\) | \(O(n+k)\) |
數據結構
數據結構 | 時間復雜度(平均) | 時間復雜度(最差) | 空間復雜度(最差) | |||||
---|---|---|---|---|---|---|---|---|
操作 | 索引 | 查找 | 插入 | 刪除 | 索引 | 查找 | 插入\刪除 | |
基本數組 | \(\Theta(1)\) | \(\Theta(n)\) | \(-\) | \(-\) | \(O(1)\) | \(O(n)\) | \(-\) | \(O(n)\) |
動態數組 | \(\Theta(1)\) | \(\Theta(n)\) | \(\Theta(n)\) | \(\Theta(n)\) | \(O(1)\) | \(O(n)\) | \(O(n)\) | \(O(n)\) |
單鏈表 | \(\Theta(n)\) | \(\Theta(n)\) | \(\Theta(1)\) | \(\Theta(1)\) | \(O(n)\) | \(O(n)\) | \(O(1)\) | \(O(n)\) |
雙鏈表 | \(\Theta(n)\) | \(\Theta(n)\) | \(\Theta(1)\) | \(\Theta(1)\) | \(O(n)\) | \(O(n)\) | \(O(1)\) | \(O(n)\) |
跳表 | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(O(n)\) | \(O(n)\) | \(O(n)\) | \(O(n\log n)\) |
哈希表 | \(-\) | \(\Theta(1)\) | \(\Theta(1)\) | \(\Theta(1)\) | \(-\) | \(O(n)\) | \(O(n)\) | \(O(n)\) |
二叉搜索樹 | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(n)\) | \(O(n)\) | \(O(n)\) | \(O(n)\) | \(O(n)\) |
笛卡爾樹 | \(-\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(-\) | \(O(n)\) | \(O(n)\) | \(O(n)\) | \(O(n)\) |
B-樹 | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(n)\) |
紅黑樹 | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(n)\) |
伸展樹 | \(-\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(-\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(n)\) |
AVL樹 | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(\Theta(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(n)\) |
堆
種類 | 時間復雜度 | ||||||
---|---|---|---|---|---|---|---|
操作 | 建堆 | 查找最大值 | 提取最大值 | 維護 | 插入 | 刪除 | 合並 |
鏈表(已排序) | \(-\) | \(O(1)\) | \(O(1)\) | \(O(n)\) | \(O(n)\) | \(O(1)\) | \(O(m+n)\) |
鏈表(未排序) | \(-\) | \(O(n)\) | \(O(n)\) | \(O(1)\) | \(O(1)\) | \(O(1)\) | \(O(1)\) |
二叉堆 | \(O(n)\) | \(O(1)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(m+n)\) |
二項堆 | \(-\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) | \(O(\log n)\) |
斐波那契堆 | \(-\) | \(O(1)\) | \(O(\log n)\) | \(O(1)\) | \(O(1)\) | \(O(\log n)\) | \(O(1)\) |
圖
管理 | 存儲 | 加點 | 加邊 | 去點 | 去邊 | 查詢 |
---|---|---|---|---|---|---|
鄰接表 | \(O(\left\|V\right\|+\left\|E\right\|)\) | \(O(1)\) | \(O(1)\) | \(O(\left\|V\right\|+\left\|E\right\|)\) | \(O(\left\|E\right\|)\) | \(O(\left\|V\right\|)\) |
關聯表 | \(O(\left\|V\right\|+\left\|E\right\|)\) | \(O(1)\) | \(O(1)\) | \(O(\left\|E\right\|)\) | \(O(\left\|E\right\|)\) | \(O(\left\|E\right\|)\) |
鄰接矩陣 | \(O(\left\|V\right\|^2)\) | \(O(\left\|V\right\|^2)\) | \(O(1)\) | \(O(\left\|V\right\|^2)\) | \(O(\left\|V\right\|^2)\) | \(O(1)\) |
關聯矩陣 | \(O(\left\|V\right\|\left\|E\right\|)\) | \(O(\left\|V\right\|\left\|E\right\|)\) | \(O(\left\|V\right\|\left\|E\right\|)\) | \(O(\left\|V\right\|\left\|E\right\|)\) | \(O(\left\|V\right\|\left\|E\right\|)\) | \(O(\left\|E\right\|)\) |
算法分析初步