1. 程式人生 > >算法分析初步

算法分析初步

找最大值 卡爾 描述 輸入 數學歸納法 漸進 pos 產生 一個

註:這個是我去年這個時候做的筆記,現在這部分內容我已經基本掌握,在博客上做個備份

算法分析

一、漸進符號

漸近分析是一種描述函數在極限附近的行為的方法,算法分析一般考慮給定算法在輸入非常大的數據集時候的性能。

定義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\]

性質

  1. 傳遞性:\(5\)個符號都具有傳遞性,\(f(n)=O(g(n))\)\(g(n)=O(h(n)) \to f(n)=O(h(n))\)
  2. 自反性:\(\Omega,\Theta,O\)記號具有自反性,\(f(n)=O(f(n))\)
  3. 對稱性:\(\Theta\)具有對稱性,\(f(n)=\Theta(g(n))\),當且僅當\(g(n)=\Theta(f(n))\)
  4. 轉置對稱性:\(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\|)\)

算法分析初步