時間複雜度的定義
一般情況下,演算法中基本操作重複執行的次數是問題規模n的某個函式,用t(n)表示,
若有某個輔助函式f(n),使得當n趨近於無窮大時,t(n)/f(n)的極限值為不等於零
的常數,則稱f(n)是t(n)的同數量級函式。記作t(n)=o(f(n)),稱o(f(n))為演算法的漸進
時間複雜度(o是數量級的符號 ),簡稱時間複雜度。
根據定義,可以歸納出基本的計算步驟
計算出基本操作的執行次數t(n)
基本操作即演算法中的每條語句(以;號作為分割),語句的執行次數也叫做語句的頻度。在做演算法分析時,一般預設為考慮最壞的情況。
令f(n)=t(n)的數量級。
用大o來表示時間複雜度
當n趨近於無窮大時,如果lim(t(n)/f(n))的值為不等於0的常數,則稱f(n)是t(n)的同數量級函式。記作t(n)=o(f(n))。
乙個示例:
(1) int num1, num2;
(2) for(int i = 0; i < n; i++)
(7) }
分析:
1. 語句int num1, num2;的頻度為1;
語句i=0;的頻度為1;
語句i < n; i++; num1 += 1; j = 1; 的頻度為n;
語句j<=n; j *= 2; num2 += num1;的頻度為n*log2n;
t(n) = 2 + 4n + 3n*log2n
2. 忽略掉t(n)中的常量、低次冪和最高次冪的係數
f(n) = n*log2n
3. lim(t(n)/f(n)) = (2+4n+3n*log2n) / (n*log2n)
= 2*(1/n)(1/log2n) + 4(1/log2n) + 3
當n趨向於無窮大,1/n趨向於0,1/log2n趨向於0
所以極限等於3。
t(n) = o(n*log2n)
簡化的計算步驟
再來分析一下,可以看出,決定演算法複雜度的是執行次數最多的語句,這裡是num2 += num1,一般也是最內迴圈的語句。
並且,通常將求解極限是否為常量也省略掉?
於是,以上步驟可以簡化為:
1. 找到執行次數最多的語句
2. 計算語句執行次數的數量級
3. 用大o來表示結果
繼續以上述演算法為例,進行分析:
1. 執行次數最多的語句為num2 += num1
2. t(n) = n*log2n
f(n) = n*log2n
3. // lim(t(n)/f(n)) = 1
t(n) = o(n*log2n)
複雜情況的分析
以上都是對於單個巢狀迴圈的情況進行分析,但實際上還可能有其他的情況,下面將例舉說明。
1.並列迴圈的複雜度分析
將各個巢狀迴圈的時間複雜度相加。
例如:
for (i=1; i
<=n; i++)
x++;
for (i=1; i
<=n; i++)
for (j=1; j
<=n; j++)
x++;
解:
第乙個for迴圈
t(n) = n
f(n) = n
時間複雜度為ο(n)
第二個for迴圈
t(n) = n2
f(n) = n2
時間複雜度為ο(n2)
整個演算法的時間複雜度為ο(n+n2) = ο(n2)。
2.函式呼叫的複雜度分析
例如:
public
void printsum(int
count)
system.out.print(sum);
}
分析:
記住,只有可執行的語句才會增加時間複雜度,因此,上面方法裡的內容除了迴圈之外,其餘的可執行語句的複雜度都是o(1)。
所以printsum的時間複雜度 = for的o(n)+o(1) = 忽略常量 = o(n)
這裡其實可以運用公式 num = n*(n+1)/2,對演算法進行優化,改為:
public void printsum(int count)
這樣演算法的時間複雜度將由原來的o(n)降為o(1),大大地提高了演算法的效能。
3.混合情況(多個方法呼叫與迴圈)的複雜度分析
例如:
public void suixiangmethod(int n)
for(int i = 0; i < n; i++)
} }
suixiangmethod 方法的時間複雜度需要計算方法體的各個成員的複雜度。
也就是1.1+1.2+1.3 = o(1)+o(n)+o(n2) —-> 忽略常數 和 非主要項 == o(n2)
更多的例子
o(1)
交換i和j的內容
temp=i;
i=j;
j=temp;
以上三條單個語句的頻度為1,該程式段的執行時間是乙個與問題規模n無關的常數。演算法的時間複雜度為常數階,記作t(n)=o(1)。如果演算法的執行時間不隨著問題規模n的增加而增長,即使演算法中有上千條語句,其執行時間也不過是乙個較大的常數。此類演算法的時間複雜度是o(1)。
o(n2)
sum=0; /* 執行次數1 */
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
sum++; /* 執行次數n2 */
解:t(n) = 1 + n2 = o(n2)
for (i = 1; i < n; i++)
解: 語句1的頻度是n-1
語句2的頻度是(n-1)*(2n+1) = 2n2-n-1
t(n) = 2n2-n-1+(n-1) = 2n2-2
f(n) = n2
lim(t(n)/f(n)) = 2 + 2*(1/n2) = 2
t(n) = o(n2).
o(n)
a=0;
b=1; ①
for (i=1;i<=n;i++) ②
解: 語句1的頻度:2,
語句2的頻度:n,
語句3的頻度:n,
語句4的頻度:n,
語句5的頻度:n,
t(n) = 2+4n
f(n) = n
lim(t(n)/f(n)) = 2*(1/n) + 4 = 4
t(n) = o(n).
o(log2n)
i=1; ①
while (i<=n)
i=i*2; ②
解: 語句1的頻度是1,
設語句2的頻度是f(n), 則:i * 2^f(n) <= n;又i=1; 故f(n)<=log2n
考慮最壞情況,取最大值f(n)=log2n,
t(n) = 1 + log2n
f(n) = log2n
lim(t(n)/f(n)) = 1/log2n + 1 = 1
t(n) = o(log2n)
o(n3)
for(i=0;i < n;i++) }
解:當i=m, j=k的時候,內層迴圈的次數為k當i=m時, j 可以取 0,1,…,m-1 , 所以這裡最內迴圈共進行了0+1+…+m-1=(m-1)m/2次所以,i從0取到n, 則迴圈共進行了: 0+(1-1)*1/2+…+(n-1)n/2=n(n+1)(n-1)/2次
t(n) = n(n+1)(n-1)/2 = (n3-n)/2
f(n) = n3
所以時間複雜度為o(n3)。
**遞迴樹法:
用遞迴樹法求t(n) = t(n/2) + n2 , 用遞迴樹法將該遞迴式展開
像這樣將遞迴樹展開並延伸下去,最終到葉子節點就只剩下t(1),那麼該遞迴樹的高度就是logn,因為從頂點n出發,到n/2,到n/4,……最後到1,那麼從n到1的折半次數是logn,即高度是logn(假設高度h,由(n/(2^h))^2 = 1得到h=lgn)。而最下面葉子節點的數目是n,因為從第一層往下,節點數變化為1,2,4,8……,如果樹的高度是h,那麼就會有2^h個葉節點,而高度是logn,那麼2^logn=n。那麼,整體所做的工作加起來就是t(n)了
t(n) = [1+1/2+1/4+……+1/(2^lgn)]n2 = 2*n^2 - n,於是可知時間複雜度為t(n) = o(n2)。
參考:
如何計算演算法的時間複雜度
求解演算法的時間複雜度的具體步驟是 找出演算法中的基本語句 演算法中執行次數最多的那條語句就是基本語句,通常是最內層迴圈的迴圈體。計算基本語句的執行次數的數量級 只需計算基本語句執行次數的數量級,這就意味著只要保證基本語句執行次數的函式中的最高次冪正確即可,可以忽略所有低次冪和最高次冪的係數。這樣能...
計算演算法複雜度
演算法的複雜度分為時間複雜度和空間複雜度 1.時間複雜度 在計算演算法複雜度時一般只用到大o符號,landau符號體系中的小o符號 符號等等比較不常用。這裡的o,最初是用大寫希臘字母,但現在都用大寫英語字母o 小o符號也是用小寫英語字母o,符號則維持大寫希臘字母 常見的演算法時間複雜度由小到大依次為...
如何計算演算法的時空複雜度
演算法 時間複雜度 時空複雜度 推薦 時間複雜度o logn o logn 的出現,一般是採用了分治的思想,演算法體現就是遞迴。logn的底數由演算法的複雜度決定 二分法,底數為2 三分法,底數是3.但是我們把logxn,logyn,x y,n取正無窮大時,會發現其實不同底數的logn只相差乙個常數...