大O表示法(复杂度分析)
程序 = 數據結構?+ 算法?
大O表示法
什么是程序?相信學過編程的人都知道,程序由數據結構和算法構成,想要寫出好的的程序,首先得了解數據結構和算法。一切脫離數據結構和算法的程序設計都是耍流氓!
什么樣的程序才是好的程序?好的程序設計無外乎兩點,"快"和"省"。"快"指程序執行速度快,高效,"省"指占用更小的內存空間。這兩點其實就對應"時間復雜度"和"空間復雜度"的問題。
怎樣分析一個程序的時間復雜度和空間復雜度?接下來今天的主角就要登場了!"大O表示法"。"大O表示法"表示程序的執行時間或占用空間隨數據規模的增長趨勢。大O表示法就是將代碼的所有步驟轉換為關于數據規模n的公式項,然后排除不會對問題的整體復雜度產生較大影響的低階系數項和常數項。乍一看這句話可能不好理解,接下來會詳細介紹怎么用"大O法"來進行時間和空間復雜度分析。
時間復雜度
時間復雜度,又稱"漸進式時間復雜度",表示代碼執行時間與數據規模之間的增長關系。
Talking is cheap,show me your code!(不BB,看代碼!)
先來看一個簡單的栗子,來分析下下面這段代碼的時間復雜度:
/*** 累加求和* @param n 數據規模 n->∞* @return*/public int sum(int n){int sum = 0;int i = 0;for (;i<n;i++){sum = sum + i;}return sum;}上面是一個累加求和的方法,假設每條語句的執行時間為單位時間unit_time,那這個方法對于數據規模n來說,總的執行時間為多少呢?
很容易看出,第7、8兩行代碼都只執行一次,各為unit_time,第9、10兩行代碼都執行n次,各為n*unit_time,所以總的時間相加得T(n)=unit_time+unit_time+n*unit_time+n*unit_time=(2n+2)*unit_time。
用f(n)來表示代碼的執行次數和數據規模的關系,即f(n)=2n+2。當n趨近于無窮大時,f(n)中的常數項對于整個公式的值的影響可以忽略,同樣,系數相比于數據規模n也可以忽略不計。最后變為f(n) = n,即總的執行時間T(n)與數據規模n成正比。上面的分析方法就是"大O表示法"的主要思想,用公式來表示就是:
?
n:數據規模,通俗點說就是函數中的那個變量n
f(n):代碼總的執行次數和數據規模的關系
T(n):代碼的執行時間(并不是代碼實際的執行時間,這里表示代碼執行時間和數據規模之間的關系)
看到這大概對大O分析法有了一定的理解吧!下面來詳細介紹時間復雜度的分析方法。
1.只關注循環執行次數最多的那段代碼
拿上面那個例子來說,第7、8行代碼與數據規模無關,執行次數最多的在第9、10行代碼,所以只需要關注循環這一部分。利用大O分析法可以知道上面累加求和例子的代碼時間復雜度為O(n).
下面再來看一個栗子:
/*** 只關注循環次數最多的那一段代碼* @param n 數據規模 n->∞* @return*/public int sums(int n){int sum = 0;int sum1 = 0;int i = 0;int j = 0;for (;i<n;i++){sum = sum + i;}for(;j<2*n;j++){sum1 = sum1 + j;}return sum+sum1;}依然拿上面累加求和這個例子,不過這次返回的是數據規模n累加和數據規模2倍累加的和。該方法中有2處循環部分,第11、12行和第14、15行。可以看出來,這兩處都有循環的部分,但是下面循環執行次數比上面多。根據上面的原則,我們只需要關注循環執行次數最多的那部分代碼。按照上面大O分析法可以知道,這段代碼的時間復雜度為O(n).
2.加法法則(總復雜度等于量級最大的那段代碼的復雜度)
首先,來了解一下量級的概念。下面是一些常見的復雜度量級及其大O法的表示。
常量階(O(1)):代碼片段執行時間不隨數據規模n的增加而增加,即使執行次數非常大,只要與數據規模無關,都算作常量階。記作O(1).
按量級遞增排序:常量階O(1)) <?對數階O(logn) <??線性階O(n)?< 線性對數階O(nlogn)?< 平方階O(n2)...立方階O(n3)...k方階 < 指數階O()?< 階乘階O(n!)?
其中,O()和O(n!)為非多項式量級,其他的為多項式量級。我們把時間復雜度為非多項式量級的算法問題叫作NP(Non-Deterministic Polynomial,非確定多項式)問題。
下面請看一段代碼:
/*** 加法法則:總復雜度等于量級最大的那段代碼的復雜度* @param n 數據規模 n->∞* @return*/public int maxItem(int n){// part1: 執行1000次循環int i = 0;int sum1 = 0;for (;i<1000;i++){ sum1 = sum1 +i;}// part2: 執行n此循環int j = 0;int sum2 = 0;for (;j<n;j++){ sum2 = sum2 +j;}// part3: 嵌套執行n次循環int sum3 = 0;for (int k=0;k<n;k++){ for (int l=0;l<n;l++){sum3 = sum3 + l;}}return sum1+sum2+sum3;}代碼中有三部分,來分別分析每一部分的時間復雜度。第一部分循環執行1000次,與數據規模n無關,即使執行10000次,100000次都算作常量階,時間復雜度為O(1)。
第二部分循環執行n次,與數據規模n成正比,時間復雜度是線性階O(n)。
第三部分有兩層循環,每層循環都執行n次,總共執行n*n=n2次,時間復雜度為平方階O(n2)。
所以這段代碼總的時間復雜度記作T(n) = O(1)+O(n)+O(n2),按照加法法則,只取最大量級的的復雜度,即整段代碼的時間復雜度為O(n2)。
3.乘法法則(嵌套代碼復雜度等于內外代碼復雜度的乘積)
依舊來看一段代碼:
/*** 外層方法* @param n 數據規模* @return*/public int outSum(int n){int i = 0;int sum = 0;for (;i<n;i++) {// 這里調用inSum()方法累加求和sum = sum + inSum(i);}return sum;}/*** 里層方法* @param n 數據規模* @return*/public int inSum(int n){int i = 0;int sum = 0;for (;i<n;i++) {sum = sum + i;}return sum;}不難分析出,但看外層和里層循環,時間復雜度均為O(n)。二者嵌套一起就可以用到乘法法則,總的時間復雜度為O(n*n)=O(n2)。其實這里就是循環嵌套,總的執行次數為內外執行次數的乘積。
空間復雜度
空間復雜度,也稱漸進空間復雜度,表示代碼存儲空間與數據規模之間的增長關系。
空間復雜度相對于時間復雜度來說,要簡單的多了。下面看一個簡單的栗子:
/*** 空間復雜度分析* @param n 數據規模*/public void space(int n){int i = 0;int[] arr = new int[n];for (;i<n;i++) {arr[i] = i;}}第6行申請一個空間存儲變量i,由于該空間和數據規模無關,屬于常量階的空間復雜度,可忽略不計。
第7行申請一個大小為n的空間存儲數組arr,空間復雜度與數據規模成正比,為O(n)。所以整段代碼空間復雜度就是O(n)。
一般情況下,一個程序在機器上執行時,除了需要存儲程序本身的指令、常數、 變量和輸入數據外,還需要存儲對數據操作的存儲單元。若輸入數據所占空間只取決于問題本身,和算法無關,這樣只需要分析該算法在實現時所需的輔助單元即可。若算法執行時所需的輔助空間相對于輸入數據而言是個常數,則稱此算法為原地工作,空間復雜度為O(1)。
常用的空間復雜度就是O(1)。像O(n2)、O(n3)、O(logn)、O(nlogn)這樣的空間復雜度平時都用不到。
以上為自己學習極客時間專欄王爭老師的"數據結構與算法"的總結,僅供學習使用。本人新開的博客,菜鳥一枚,不正確的地方希望大家指出來,一起學習進步!
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的大O表示法(复杂度分析)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 8、spss做因子分析及主成分分析
- 下一篇: 【学习日志】2022.08.26 C#单