時間複雜度AND空間複雜度專項
本文參考:https://www.cnblogs.com/coder-programming/p/11093608.html
時間維度:是指執行當前算法所消耗的時間,我們通常用「時間複雜度」來描述。
空間維度:是指執行當前算法需要佔用多少內存空間,我們通常用「空間複雜度」來描述。
時間複雜度
一個算法花費的時間與算法中語句的執行次數成正比例,哪個算法中語句執行次數多,它花費時間就多。 一個算法中的語句執行次數稱爲語句頻度或時間頻度。記爲 T(n)。
常見的算法時間複雜度由小到大依次爲:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<Ο(nk)<Ο(2n) ,隨着問題規模 n 的不斷增大,上述時間複雜度不斷增大,算法的執行效率越低
常見的時間複雜度:
常見的時間複雜度:
常數階 O(1)
對數階 O(log2n)
線性階 O(n)
線性對數階 O(nlog2n)
平方階 O(n^2)
立方階 O(n^3)
k 次方階 O(n^k)
指數階 O(2^n)
常見階數串講
-
常數階 O(1)
- 沒有循環結構,代碼上萬行都可以,消耗並不伴隨某個的增長而增長,都是O(1)
-
對數階O(log2n)
- 舉個例子
int n=1000; int i=1; while(i<=n){ i=i*2; } cout<<"啦啦啦啦i="<<i<<endl;
看在while循環中執行了多少次:while終止條件是i>n的時候,即當2的x次方等於n時結束循環,那麼顯然x=log2n。也就是說while循環執行了log2n次後就結束了,那麼這個算法的時間複雜度就是log2n。
從這個例子可以看出,如果將循環改成
i=i*3;
那麼複雜度自然就變成了log3n。
-
線性階O(n)
現在你已經基本入門啦,我們直接上例子,線性階很好理解,就是在循環中變量增長從倍數變成了單個增長。
int n=1000; int i=1; while(i<=n){ i++; } cout<<"啦啦啦啦i="<<i<<endl;
顯然i需要增加n次纔可以執行結束,故時間複雜度爲O(n)
-
線性對數階O(nlogN)
套娃!外層循環執行n次,內部循環需要執行logN次,那麼一共就是n*logN啦,故時間複雜度爲O(nlogN)。
繼續上代碼:
int j=0; for(int i=1;i<=n;i++){ j=1; while(j<n){ j=j*2;//時間複雜度爲O(log2n) } }
這個時間複雜度就是O(nlog2n)
-
平方階O(n²)
平方階和線性對數階類似,也是套娃!
外層n,內層n不就成n²了嗎!
如果外層m,內層n,那麼就是O(mn)
-
立方階O(n³)、K次方階O(n^k)
類似~
運算法則
以上舉例都是套娃或者單個循環的,如果遇到多個怎麼辦?這就涉及到時間複雜度計算法則了:
只關注循環執行次數最多的一段代碼
加法法則:總複雜度等於量級最大的那段代碼的複雜度
如果 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))).
乘法法則:嵌套代碼的複雜度等於嵌套內外代碼複雜度的乘積
T(n) = T1(n) * T2(n) = O(n*n) = O(n2)
平均時間複雜度和最壞時間複雜度
- 平均時間複雜度:所有可能輸入實例均以等概率出現的情況下,該算法的運行時間。
- 最壞時間複雜度:最壞情況下(即算法運行時間最長的情況)
- 平均時間複雜度與最壞時間複雜度是否一致,與算法有關。
空間複雜度
類似於時間複雜度,一個算法的空間複雜度定義爲該算法所耗費的存儲空間,也是問題規模n的函數
(別問,上面一句話我沒看懂。。。)
可以理解爲,空間複雜度是用來評判一個算法的執行所需佔用的臨時存儲空間。在做算法分析的時候,主要討論的是時間複雜度。從用戶體驗上看,更看重程序的執行速度,所以有時候爲了提升算法速度使用空間來換時間。
突然想到那種變態排序,1~2015範圍,直接開闢2016的數組大小,有數的就標記爲1其餘爲0,然後遍歷輸出。。。時間複雜度:O(n)
定義
算法的空間複雜度的計算公式記作:S(n)=O(f(n)),其中,n爲問題的規模,f(n)爲語句關於n所佔存儲空間的函數。
舉個例子:
void print(int n) {
int i = 0;//常量階,不算問題規模
int[] a = new int[n];//規模爲n
for (i; i <n; ++i) {
a[i] = i * i;
}
for (i = n-1; i >= 0; --i) {
print out a[i]
}
}
顯然代碼開闢了一個規模爲n的數組,故整體的空間複雜度爲O(n)