時間複雜度和空間複雜度是我們算法效率的度量方法。
時間複雜度我們用大O表示法,比如O(1),O(n),O(logn),O(n2)等,那麼這個是怎麼計算出來的呢。
簡單來說就是看某段代碼的執行次數。
注意:
1.如果是常數級別的都是O(1),這裏的常數是指的我們已經很確定這段代碼執行多少次,不存在變化了。
2.指數級的時候取最高階,比如n2+n,那麼是O(n2);
3.常數我們通常忽略。
O(1):
int a = 1; for(int i = 0 ;i < 1000;i++){ a = a + 1; }
這段代碼的時間複雜度就是O(1),不管他執行多少次,已經十分確定它是某一個值了
O(n):
for(int i = 0 ;i < n;i++){ a = a + 1; }
這段代碼n是個未知數,因此它的時間複雜度是O(n)
O(logn):
int i = 1; while( i <= n){ i = i * 3; }
這段代碼實際的執行次數,就是求3x=n,求出x=log3n(3爲底n的對數),忽略掉常數3,時間複雜度表示爲O(logn)
O(n2):
for(i = 0 ; i < n;i++){ for(int j = 0 ; j < n ;j ++){ a = a +1; } }
for(i = 0 ; i <= n;i++){ for(int j = i ; j <= n ;j ++){ a = a +1; } }
第一段代碼很好計算直接就是O(n2);
第二段代碼
i=n 運行1次
i=n-1 運行2次
i=n-2 運行3次
......
i=1 運行n次
明顯是個等差數列,總次數就是等差數列求和sn=n(n+1)/2=(n2+n)/2,忽略掉常數和低階,那麼就是O(n2)
空間複雜度
其實對於空間複雜度,我們經常都會用空間去換時間,比如一個List<Student>,要根據Student的ID去list裏面取元素,這種情況下我們並不知道index,每次都需要遍歷判斷,那麼每次取元素的時間複雜度都是O(n);我們就可以將ID作爲key,把List轉換成Map,每次從map中取就是O(1)了,但是這裏多出來了一個Map,會佔用更多內存空間。這就是用空間換時間。一般我們說的複雜度都是指的時間複雜度。
算法之美案例:判斷一個數是否是2的冪
算法1:
while(n > 1){ if (n % 2 == 0) { return true; } n = n / 2; } return false;
無限除以2再取模判斷是否等於0,如果最後取模2等於0,那麼說明是2的次冪。時間複雜度O(logn)
算法2:
if (n <= 0) { return false; } return (n & (n-1)) == 0;
2的二進制表示:10 1的二進制:1
4的二進制表示:100 3的二進制:11
8的二進制表示:1000 7的二進制:111
利用&運算,如果n是2的次冪,那麼n&(n-1)剛好等於0,這個算法時間複雜度O(1)。
明顯算法2的性能是比算法1的性能好的。
時間複雜度優劣排行:O(1)>O(logn)>O(n)>O(nlogn)>O(n^2)>O(n^x)