1. 程式人生 > >演算法複雜度分析(時間複雜度,空間複雜度)

演算法複雜度分析(時間複雜度,空間複雜度)

前幾天被問到虛擬DOM的時間複雜度,一臉蒙圈,什麼是時間複雜度,我可能大學的資料結構課都在睡覺吧,今天來看看巨人的肩膀。
為什麼要進行演算法分析?

預測演算法所需的資源:
	計算時間(CPU消耗)
	記憶體空間(RAM消耗)
	通訊時間(頻寬消耗)
預測演算法的執行時間:
	在給定輸入規模時,所執行的基本運算元量
	或者稱之為演算法複雜度

如何衡量演算法複雜度?

記憶體(memory)
時間(time)
指令的數量(number of steps)
特定操作的數量:
	磁碟訪問數量
	網路包數量
漸進複雜度(Asymptotic Complexity)

演算法的執行時間與什麼相關?

取決於輸入的資料(例如:資料如果是排好序的,時間消耗可能會減少)
取決於輸入資料的規模(例如:6和6*10^9)
取決於執行時間的上限

演算法分析的種類:

最壞情況:任意輸入規模的最大執行時間
平均情況:任意輸入規模的期待雲執行時間
最佳情況:通常不會出現

演算法分析要保持大局觀,其基本思路是:

1,忽略掉那些依賴於機器的常量,
2,關注執行時間的增長趨勢。

計算程式碼塊的漸近執行方式有如下步驟:

1,確定決定演算法執行時間的組成部分
2,找到執行該步驟的程式碼,標記為1
3,檢視標記為1的程式碼的下一行程式碼,如果下一行程式碼是一個迴圈,則將標記1修改為1倍於迴圈的次數1*n,
如果包含多個巢狀的迴圈,則將繼續計算倍數,例如1*n*m
4,找到標記到的最大的值,就是執行時間的最大值,即演算法複雜度描述的上界。
                                                                                                                      

複雜度				標記符號		描述

常量(Constant)		O(1) 		操作的數量為常數,與輸入的資料的規模無關。n = 1,000,000 -> 1-2 operations 
對數(Logarithmic)	O(log2 n) 	操作的數量與輸入資料的規模 n 的比例是 log2 (n)。n = 1,000,000 -> 30 operations
線性(Linear)	 	O(n)		操作的數量與輸入資料的規模 n 成正比。n = 10,000 -> 5000 operations
平方(Quadratic)	O(n2)		操作的數量與輸入資料的規模 n 的比例為二次平方。n = 500 -> 250,000 operations
立方(Cubic)	    O(n3)		操作的數量與輸入資料的規模 n 的比例為三次方。n = 200 -> 8,000,000 operations
指數(Exponential)	O(2n)
					O(kn)
					O(n!)		指數級的操作,快速的增長。n = 20 -> 1048576 operations        

注1:快速的數學回憶,logab = y 其實就是 ay = b。所以,log24 = 2,因為 22 = 4。
同樣 log28 = 3,因為 23 = 8。我們說,log2n 的增長速度要慢於 n,因為當 n = 8 時,log2n = 3。
注2:通常將以 10 為底的對數叫做常用對數。為了簡便,N 的常用對數 log10 N 簡寫做 lg N,例如 log10 5 記做 lg 5。
注3:通常將以無理數 e 為底的對數叫做自然對數。為了方便,N 的自然對數 loge N 簡寫做 ln N,例如 loge 3 記做 ln 3。
注4:在演算法導論中,採用記號 lg n = log2 n ,也就是以 2 為底的對數。
改變一個對數的底只是把對數的值改變了一個常數倍,所以當不在意這些常數因子時,我們將經常採用 "lg n"記號,就像使用 O 記號一樣。
計算機工作者常常認為對數的底取 2 最自然,因為很多演算法和資料結構都涉及到對問題進行二分。
O(1)<O(log2(n))<O(n)<O(n*log2(n))<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)

時間複雜度
時間複雜度的計算並不是計算程式具體執行的時間,而是演算法執行語句的次數。
計算方法:
①選取相對增長最高的項
②最高項係數是都化為1
③若是常數的話用O(1)表示
如f(n)=2*n^3+2n+100,
則O(n)=n^3
通常我們計算時間複雜度都是計算最壞情況
時間複雜度的計算:
(1)如果演算法的執行時間不隨著問題規模n的增加而增長,即使演算法中有上千條語句,其執行時間也不過是一個較大的常數。此類演算法的時間複雜度是O(1)。

int x=1;
while (x <10)
{
    x++;
}

該演算法執行次數是10,是一個常數,用時間複雜度表示是O(1)。

(2)當有若干個迴圈語句時,演算法的時間複雜度是由巢狀層數最多的迴圈語句中最內層語句的頻度f(n)決定的。

for (i = 0; i < n; i++)
{
    for (j = 0; j < n; j++)
    {
        ;
    }
}

該演算法for迴圈,最外層迴圈每執行一次,內層迴圈都要執行n次,執行次數是根據n所決定的,時間複雜度是O(n^2)。

(3)迴圈不僅與n有關,還與執行迴圈所滿足的判斷條件有關。

int i=0;
while (i < n && arr[i]!=1)
{
    i++;
}

在此迴圈,如果arr[i]不等於1的話,時間複雜度是O(n)。如果arr[i]等於1的話,則迴圈不能執行,時間複雜度是0。

空間複雜度
空間複雜度是對一個演算法在執行過程中臨時佔用儲存空間大小的量度。
計算方法:
①忽略常數,用O(1)表示
②遞迴演算法的空間複雜度=遞迴深度N*每次遞迴所要的輔助空間
③對於單執行緒來說,遞迴有執行時堆疊,求的是遞迴最深的那一次壓棧所耗費的空間的個數,因為遞迴最深的那一次所耗費的空間足以容納它所有遞迴過程。
如:

int a;
int b;
int c;
printf("%d %d %d \n",a,b,c);

它的空間複雜度O(n)=O(1);

int fun(int n,)
{
	int k=10;
	if(n==k)
		return n;
	else
		return fun(++n);
}

遞迴實現,呼叫fun函式,每次都建立1個變數k。呼叫n次,空間複雜度O(n*1)=O(n)。

膜拜大佬~~
https://www.cnblogs.com/TangBiao/p/5856695.html
https://blog.csdn.net/halotrriger/article/details/78994122