日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【数据结构与算法-java实现】一 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?

發布時間:2023/12/10 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【数据结构与算法-java实现】一 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天開始學習程序的靈魂:數據結構與算法。

本文是自己學習極客時間專欄-數據結構與算法之美后的筆記總結。如有侵權請聯系我刪除文章。

我們都知道,數據結構和算法本身解決的是“快”和“省”的問題,即如何讓代碼運行得更快,如何讓代碼更省存儲空間。所以,執行效率是算法一個非常重要的考量指標。那如何來衡量你編寫的算法代碼的執行效率呢?這里就要用到我們今天要講的內容:時間、空間復雜度分析。

復雜度分析是整個算法學習的精髓,只要掌握了它,數據結構和算法的內容基本上就掌握了一半。

1、大O復雜度表示法

算法的執行效率,粗略的說,就是代碼的執行時間。但是實際上代碼在被CPU執行的時候,是相當快的,這個時間我們也無法計算。所以就抽象出了一個用肉眼能夠看到的時間。以例子來分析,看如下一個求和的代碼:

int cal(int n) {int sum = 0;int i = 1;for (; i <= n; ++i) {sum = sum + i;}return sum;}

對于CPU來說,它只知道從內存中取指令與執行指令。所以上述代碼,CPU就是一條一條的取指令與執行該指令。現在假設CPU執行每一條的指令的時間都是一樣的為:p_time。那么上述代碼第二三行執行時間2*p_time,四五六行是一個循環。所以第四五行的執行時間是2n*p_time。所以總的執行時間是: (2n+2)*p_time

可以看到:所有代碼的執行時間 T(n) 與每行代碼的執行次數成正比。

按照上述思路,我們再來分析以下代碼:

int cal(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;}}}

整段代碼總的執行時間 T(n) = (2n2+2n+3)*p_time。

這里我們雖然不知道p_time的具體值,但是很明顯,代碼的總的執行時間T(n)是與n成正比(這里的正比,不是數學的正比,是隨著n的增大,T(n)越來越大)。

我們可以把這種規律,總結成一個規律。此時大O就出現了。

T(n)=O(f(n))

T(n) 表示代碼執行的時間;n 表示數據規模的大小;f(n) 表示每行代碼執行的次數總和。因為這是一個公式,所以用 f(n) 來表示。公式中的 O,表示代碼的執行時間 T(n) 與 f(n) 表達式成正比。

所以,第一個例子中的 T(n) = O(2n+2),第二個例子中的 T(n) = O(2n2+2n+3)。這就是大 O 時間復雜度表示法。

注意:大O時間復雜度表示法,并不代表代碼的真正執行時間,而是代表執行的時間隨數據規模增長的一種變化趨勢。 簡稱時間復雜度

為了簡化時間復雜度的表示,以及由于一些常數,系數以及量級比較小的項對整體的變化影響不大,所以一般將他們去掉。那么以上兩種例子的時間復雜度在簡化以后就是:T(n)=O(n)和T(n)=O(n2)。

2、時間復雜度分析

遇到一段代碼,如何分析它的時間復雜度。一般有三種方法

2.1 只關注循環次數最多的一段代碼

例如如下代碼:

int cal(int n) {int sum = 0;int i = 1;for (; i <= n; ++i) {sum = sum + i;}return sum;}

它的時間復雜度就位:T(n)=O(n)

2.2、加法法則

總的時間復雜度等于量級最大的那段代碼的時間復雜度。

看如下代碼:

int cal(int n) {int sum_1 = 0;int p = 1;for (; p < 100; ++p) {sum_1 = sum_1 + p;}int sum_2 = 0;int q = 1;for (; q < n; ++q) {sum_2 = sum_2 + q;}int sum_3 = 0;int i = 1;int j = 1;for (; i <= n; ++i) {j = 1; for (; j <= n; ++j) {sum_3 = sum_3 + i * j;}}return sum_1 + sum_2 + sum_3;}

上述代碼中,第一段代碼的循環為100次,第二段代碼的循環為n次,第三段代碼的時間復雜度為n2次。此時,要注意一點,任何常數次循環,都是O(1時間復雜度),不管是100次,10000次,10000000次,只要能看出是一個具體的數字,它都是O(1)時間復雜度。

由總的時間復雜度等于量級最大的那段代碼的時間復雜度。所以上述代碼最終時間復雜度為O(n2

結論:
如果 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))).

2.3、乘法法則

嵌套代碼的復雜度等于嵌套內外代碼復雜度的乘積

上面講了一個復雜度分析中的加法法則,這兒還有一個乘法法則。類比一下,你應該能“猜到”公式是什么樣子的吧?

如果 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)).

也就是說,假設 T1(n) = O(n),T2(n) = O(n2),則 T1(n) * T2(n) = O(n3)。落實到具體的代碼上,我們可以把乘法法則看成是嵌套循環,舉個例子給你解釋一下。

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;}

我們單獨看 cal() 函數。假設 f() 只是一個普通的操作,那第 4~6 行的時間復雜度就是,T1(n) = O(n)。但 f() 函數本身不是一個簡單的操作,它的時間復雜度是 T2(n) = O(n),所以,整個 cal() 函數的時間復雜度就是,T(n) = T1(n) * T2(n) = O(n*n) = O(n2)。

3、幾種常見復雜度分析

3.1、O(1)時間復雜度

如果代碼中沒有循環或者循環的次數是可以確定的常數,那么就是O(1)復雜度

3.2、O(logn) O(nlogn)

看下面的代碼:

i=1;while (i <= n) {i = i * 2;}

設執行的次數為x,則2x=n。解得x=log2n

再看下面的代碼:

i=1;while (i <= n) {i = i * 3;}

設執行的次數為x,3x=n。解得x=log3n

實際上,不管是以 2 為底、以 3 為底,還是以 10 為底,我們可以把所有對數階的時間復雜度都記為 O(logn)。為什么呢?

我們知道,對數之間是可以互相轉換的,log3n 就等于 log32 * log2n,所以 O(log3n) = O(C * log2n),其中 C=log32 是一個常量。基于我們前面的一個理論:在采用大 O 標記復雜度的時候,可以忽略系數,即 O(Cf(n)) = O(f(n))。所以,O(log2n) 就等于 O(log3n)。因此,在對數階時間復雜度的表示方法里,我們忽略對數的“底”,統一表示為 O(logn)。

如果你理解了前面講的 O(logn),那 O(nlogn) 就很容易理解了。還記得剛講的乘法法則嗎?如果一段代碼的時間復雜度是 O(logn),我們循環執行 n 遍,時間復雜度就是 O(nlogn) 了。而且,O(nlogn) 也是一種非常常見的算法時間復雜度。比如,歸并排序、快速排序的時間復雜度都是 O(nlogn)。

3.1、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; }

從代碼中可以看出,m 和 n 是表示兩個數據規模。我們無法事先評估 m 和 n 誰的量級大,所以我們在表示復雜度的時候,就不能簡單地利用加法法則,省略掉其中一個。所以,上面代碼的時間復雜度就是 O(m+n)。

針對這種情況,原來的加法法則就不正確了,我們需要將加法規則改為:T1(m) + T2(n) = O(f(m) + g(n))。但是乘法法則繼續有效:T1(m)*T2(n) = O(f(m) * f(n))。

4、空間復雜度的分析

前面,花了很長時間講大 O 表示法和時間復雜度分析,理解了前面講的內容,空間復雜度分析方法學起來就非常簡單了。

前面我講過,時間復雜度表示算法的執行時間與數據規模之間的增長關系。類比一下,空間復雜度表示算法的存儲空間與數據規模之間的增長關系。

看如下代碼:

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]} }

跟時間復雜度分析一樣,我們可以看到,第 2 行代碼中,我們申請了一個空間存儲變量 i,但是它是常量階的,跟數據規模 n 沒有關系,所以我們可以忽略。第 3 行申請了一個大小為 n 的 int 類型數組,除此之外,剩下的代碼都沒有占用更多的空間,所以整段代碼的空間復雜度就是 O(n)。

我們常見的空間復雜度就是 O(1)、O(n)、O(n2 ),像 O(logn)、O(nlogn) 這樣的對數階復雜度平時都用不到。而且,空間復雜度分析比時間復雜度分析要簡單很多。

5、總結

復雜度也叫漸進復雜度,包括時間復雜度和空間復雜度,用來分析算法執行效率與數據規模之間的增長關系,可以粗略地表示,越高階復雜度的算法,執行效率越低。常見的復雜度并不多,從低階到高階有:O(1)、O(logn)、O(n)、O(nlogn)、O(n2 )。

本文是自己學習極客時間專欄-數據結構與算法之美后的筆記總結。如有侵權請聯系我刪除文章。

學習探討加個人:
qq:1126137994
微信:liu1126137994

總結

以上是生活随笔為你收集整理的【数据结构与算法-java实现】一 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。