复杂度分析学习总结
復(fù)雜度分析學(xué)習(xí)總結(jié)
1、大 O 復(fù)雜度表示法
T(n) = O( f(n) )
其中,T(n) 表示代碼執(zhí)行的時(shí)間;n 表示數(shù)據(jù)規(guī)模的大小;f(n) 表示每行代碼執(zhí)行的次數(shù)總和。公式中的 O,表示代碼的執(zhí)行時(shí)間 T(n) 與 f(n) 表達(dá)式成正比。
大 O 時(shí)間復(fù)雜度實(shí)際上并不具體表示代碼真正的執(zhí)行時(shí)間,而是表示代碼執(zhí)行時(shí)間隨數(shù)據(jù)規(guī)模增長(zhǎng)的變化趨勢(shì),所以,也叫作漸進(jìn)時(shí)間復(fù)雜度(asymptotic time complexity),簡(jiǎn)稱時(shí)間復(fù)雜度。
int cal1(int n) {int sum = 0;int i = 1;for (; i <= n; ++i) {sum = sum + i;}return sum;}int cal2(int n) {int sum = 0;int i = 1;int j = 1;for (; i <= n; ++i) {j = 1;for (; j <= n; ++j) {sum = sum + i * j;}}}上面例子中,第一個(gè)的 T(n) = O(2n+2),第二個(gè)的 T(n) = O(2n2+2n+3),當(dāng) n 很大時(shí),可以把它想象成 10000、100000。而公式中的低階、常量、系數(shù)三部分并不左右增長(zhǎng)趨勢(shì),所以都可以忽略。只需要記錄一個(gè)最大量級(jí)就可以了,如果用大 O 表示法表示剛講的那兩段代碼的時(shí)間復(fù)雜度,就可以記為:T(n) = O(n); T(n) = O(n^2)。
2、時(shí)間復(fù)雜度分析方法
(1) 只關(guān)注循環(huán)執(zhí)行次數(shù)最多的一段代碼
(2) 加法法則:總復(fù)雜度等于量級(jí)最大的那段代碼的復(fù)雜度
這個(gè)規(guī)律抽象成公式就是:
如果 T1(n)=O(f(n)),T2(n)=O(g(n));那么 T(n)=T1(n)+T2(n)=max(O(f(n)), O(g(n))) =O(max(f(n), g(n)))
(3)乘法法則:嵌套代碼的復(fù)雜度等于嵌套內(nèi)外代碼復(fù)雜度的乘積
這個(gè)規(guī)律抽象成公式就是:
如果 T1(n)=O(f(n)),T2(n)=O(g(n));那么 T(n)=T1(n)*T2(n)=O(f(n))*O(g(n))=O(f(n)*g(n))
舉個(gè)例子:
int cal(int n) {int ret = 0; int i = 1;for (; i < n; ++i) {ret = ret + f(i);} } int f(int n) {int sum = 0;int i = 1;for (; i < n; ++i) {sum = sum + i;} return sum;}如果單獨(dú)看 cal() 函數(shù),假設(shè) f() 只是一個(gè)普通的操作,那第 4~6 行的時(shí)間復(fù)雜度就是,T1(n) = O(n)。但 f() 函數(shù)本身不是一個(gè)簡(jiǎn)單的操作,它的時(shí)間復(fù)雜度是 T2(n) = O(n),所以,整個(gè) cal() 函數(shù)的時(shí)間復(fù)雜度就是,T(n) = T1(n) * T2(n) = O(n*n) =O(n^2)。
?
3、幾種常見(jiàn)時(shí)間復(fù)雜度實(shí)例分析
可以粗略的分為:多項(xiàng)式量級(jí)和非多項(xiàng)式量級(jí)
(1)非多項(xiàng)式量級(jí)
非多項(xiàng)式量級(jí)只有兩個(gè):O(2n) 和 O(n!)。
我們把時(shí)間復(fù)雜度為非多項(xiàng)式量級(jí)的算法問(wèn)題叫作 NP(Non-Deterministic Polynomial,非確定多項(xiàng)式)問(wèn)題。當(dāng)數(shù)據(jù)規(guī)模 n 越來(lái)越大時(shí),非多項(xiàng)式量級(jí)算法的執(zhí)行時(shí)間會(huì)急劇增加,求解問(wèn)題的執(zhí)行時(shí)間會(huì)無(wú)限增長(zhǎng)。所以,非多項(xiàng)式時(shí)間復(fù)雜度的算法其實(shí)是非常低效的算法。
(2)多項(xiàng)式量級(jí)
一般常見(jiàn)的,從低階到高階有:O(1)、O(logn)、O(n)、O(nlogn)、O(n^2)
時(shí)間復(fù)雜度趨勢(shì)圖如下:
其中O(logn)、O(nlogn)比較特殊,如下例子可以說(shuō)明
i=1;while (i <= n) {i = i * 2;} // 這段代碼的時(shí)間復(fù)雜度就是 O(log2 n)i=1; while (i <= n) { i = i * 3; } // 這段代碼的時(shí)間復(fù)雜度就是 O(log3 n)在對(duì)數(shù)階時(shí)間復(fù)雜度的表示方法里,可以忽略對(duì)數(shù)的“底”,統(tǒng)一表示為 O(logn)
有種情況是,如果我們無(wú)法事先評(píng)估 m 和 n 誰(shuí)的量級(jí)大,所以我們?cè)诒硎緩?fù)雜度的時(shí)候,就不能簡(jiǎn)單地利用加法法則,省略掉其中一個(gè),如下例子的時(shí)間復(fù)雜度是:O(m+n)、O(m*n)
int cal(int m, int n) {int sum_1 = 0;int i = 1;for (; i < m; ++i) {sum_1 = sum_1 + i;}int sum_2 = 0;int j = 1;for (; j < n; ++j) {sum_2 = sum_2 + j;}return sum_1 + sum_2; }?
4、空間復(fù)雜度分析
定義:時(shí)間復(fù)雜度的全稱是漸進(jìn)時(shí)間復(fù)雜度,表示算法的執(zhí)行時(shí)間與數(shù)據(jù)規(guī)模之間的增長(zhǎng)關(guān)系。類比一下,空間復(fù)雜度全稱就是漸進(jìn)空間復(fù)雜度(asymptotic space complexity),表示算法的存儲(chǔ)空間與數(shù)據(jù)規(guī)模之間的增長(zhǎng)關(guān)系
空間復(fù)雜度的定義的比較簡(jiǎn)單,例子如下
void print(int n) {int i = 0;int[] a = new int[n];for (i; i <n; ++i) {a[i] = i * i;}for (i = n-1; i >= 0; --i) {print out a[i]} }跟時(shí)間復(fù)雜度分析一樣,可以看到,第 2 行代碼中,我們申請(qǐng)了一個(gè)空間存儲(chǔ)變量 i,但是它是常量階的,跟數(shù)據(jù)規(guī)模 n 沒(méi)有關(guān)系,所以我們可以忽略。第 3 行申請(qǐng)了一個(gè)大小為 n 的 int 類型數(shù)組,除此之外,剩下的代碼都沒(méi)有占用更多的空間,所以整段代碼的空間復(fù)雜度就是 O(n)。
?
5、四個(gè)復(fù)雜度分析方面知識(shí)點(diǎn)
最好情況時(shí)間復(fù)雜度(best case time complexity)、最壞情況時(shí)間復(fù)雜度(worst case time complexity)、平均情況時(shí)間復(fù)雜度(average case time complexity)、均攤時(shí)間復(fù)雜度(amortized time complexity)
(1)最好情況時(shí)間復(fù)雜度、最壞情況時(shí)間復(fù)雜度
顧名思義,最好情況時(shí)間復(fù)雜度就是,在最理想的情況下,執(zhí)行這段代碼的時(shí)間復(fù)雜度,最壞情況時(shí)間復(fù)雜度就是,在最糟糕的情況下,執(zhí)行這段代碼的時(shí)間復(fù)雜度
(2)平均情況時(shí)間復(fù)雜度、均攤時(shí)間復(fù)雜度
平均時(shí)間復(fù)雜度的全稱又叫加權(quán)平均時(shí)間復(fù)雜度或者期望時(shí)間復(fù)雜度。
// n表示數(shù)組array的長(zhǎng)度 int find(int[] array, int n, int x) {int i = 0;int pos = -1;for (; i < n; ++i) {if (array[i] == x) {pos = i;break;}}return pos; }要查找的變量 x 在數(shù)組中的位置,有 n+1 種情況:在數(shù)組的 0~n-1 位置中和不在數(shù)組中。把每種情況下,查找需要遍歷的元素個(gè)數(shù)累加起來(lái),然后再除以 n+1,就可以得到需要遍歷的元素個(gè)數(shù)的平均值,即:
時(shí)間復(fù)雜度的大 O 標(biāo)記法中,可以省略掉系數(shù)、低階、常量,所以,咱們把剛剛這個(gè)公式簡(jiǎn)化之后,得到的平均時(shí)間復(fù)雜度就是 O(n)。這個(gè)結(jié)論雖然是正確的,但是計(jì)算過(guò)程稍微有點(diǎn)兒?jiǎn)栴}。這 n+1 種情況,出現(xiàn)的概率并不是一樣的。要查找的變量 x,要么在數(shù)組里,要么就不在數(shù)組里。假設(shè)在數(shù)組中與不在數(shù)組中的概率都為 1/2。另外,要查找的數(shù)據(jù)出現(xiàn)在 0~n-1 這 n 個(gè)位置的概率也是一樣的,為 1/n。所以,根據(jù)概率乘法法則,要查找的數(shù)據(jù)出現(xiàn)在 0~n-1 中任意位置的概率就是 1/(2n)。因此,如果平均時(shí)間復(fù)雜度為O(n)的最大問(wèn)題就是,沒(méi)有將各種情況發(fā)生的概率考慮進(jìn)去。
如果把每種情況發(fā)生的概率也考慮進(jìn)去,那平均時(shí)間復(fù)雜度的計(jì)算過(guò)程就變成了這樣:
這個(gè)值就是概率論中的加權(quán)平均值,也叫作期望值,所以平均時(shí)間復(fù)雜度的全稱應(yīng)該叫加權(quán)平均時(shí)間復(fù)雜度或者期望時(shí)間復(fù)雜度。引入概率之后,前面那段代碼的加權(quán)平均值為 (3n+1)/4。用大 O 表示法來(lái)表示,去掉系數(shù)和常量,這段代碼的加權(quán)平均時(shí)間復(fù)雜度仍然是 O(n)。
?
均攤時(shí)間復(fù)雜度
// array表示一個(gè)長(zhǎng)度為n的數(shù)組// 代碼中的array.length就等于nint[] array = new int[n];int count = 0;void insert(int val) {if (count == array.length) {int sum = 0;for (int i = 0; i < array.length; ++i) {sum = sum + array[i];}array[0] = sum;count = 1;}array[count] = val;++count;}在數(shù)組中插入數(shù)據(jù)的這個(gè)例子。每一次 O(n) 的插入操作,都會(huì)跟著 n-1 次 O(1) 的插入操作,所以把耗時(shí)多的那次操作均攤到接下來(lái)的 n-1 次耗時(shí)少的操作上,均攤下來(lái),這一組連續(xù)的操作的均攤時(shí)間復(fù)雜度就是 O(1)
均攤時(shí)間復(fù)雜度就是一種特殊的平均時(shí)間復(fù)雜度,均攤時(shí)間復(fù)雜度和攤還分析應(yīng)用場(chǎng)景比較特殊,所以并不會(huì)經(jīng)常用到,在能夠應(yīng)用均攤時(shí)間復(fù)雜度分析的場(chǎng)合,一般均攤時(shí)間復(fù)雜度就等于最好情況時(shí)間復(fù)雜度。
?
總結(jié)
- 上一篇: 处理业务代码中循环遍历出现的性能问题
- 下一篇: 递归学习总结