這是我參與8月更文挑戰的第1天,活動詳情查看:8月更文挑戰算法
刷算法的小夥伴都知道,算法有好有壞,咱們刷算法的最高的一個追求是尋找一個最優解,那麼咱們怎麼評判一個算法的好壞呢?那就是算法運行時間的長短和佔用內存空間的大小 ,對應時間複雜度跟空間複雜度。數組
既然要衡量好壞那麼確定要有一種方法,咱們算法中經常使用大O(字母)表示法來衡量算法的時間複雜度(也稱漸進時間複雜度)和空間複雜度(也稱漸進空間複雜度)。那麼大O表示法是什麼呢?咱們從時間複雜度角度看,其定義以下:markdown
若存在函數f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值爲 不等於零的常數,則稱f(n)是T(n)的同數量級函數。記做app
T(n)=O(f(n)),稱爲O(f(n)),O爲算法的漸進時間複雜度,簡稱爲時間複雜度ide
常見的時間/空間複雜度量級有:函數
下面經過經過舉一些例子來進行經常使用時間/空間複雜度的判斷。post
在 大O符號表示法中,時間複雜度的公式是: T(n) = O( f(n) ),其中f(n) 表示每行代碼執行次數之和,而 O 表示正比例關係 。ui
不管代碼執行了多少行,只要是沒有循環等複雜結構,那這個代碼的時間複雜度就都是O(1),如:lua
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
複製代碼
線性階通常爲執行n次的循環或者執行n次遞歸,如:url
for(i=1; i<=n; ++i)
{
j = i;
j++;
}
複製代碼
仍是先來看代碼:
int i = 1;
while(i<n)
{
i = i * 2;
}
複製代碼
從上面代碼能夠看到,在while循環裏面,每次都將 i 乘以 2,乘完以後,i 距離 n 就愈來愈近了。咱們試着求解一下,假設循環x次以後,i 就大於 2 了,此時這個循環就退出了,也就是說 2 的 x 次方等於 n,那麼 x = log2^n 也就是說當循環 log2^n 次之後,這個代碼就結束了。所以這個代碼的時間複雜度爲:O(logn)
線性對數階O(nlogN) 其實很是容易理解,將時間複雜度爲O(logn)的代碼循環N遍的話,那麼它的時間複雜度就是 n * O(logN),也就是O(nlogN)。
就拿上面的代碼加一點修改來舉例 :
for(m=1; m<n; m++)
{
i = 1;
while(i<n)
{
i = i * 2;
}
}
複製代碼
平方階O(n²) 就更容易理解了,若是把 O(n) 的代碼再嵌套循環一遍,它的時間複雜度就是 O(n²) 了。 舉例:
for(x=1; i<=n; x++)
{
for(i=1; i<=n; i++)
{
j = i;
j++;
}
}
複製代碼
空間複雜度是對一個算法在運行過程當中臨時佔用存儲空間大小的一個量度,一樣反映的是一個趨勢,它的公式爲:S(n)=O(f(n))。
空間複雜度比較經常使用的有:O(1)、O(n)、O(n²),下面咱們舉例看看。
當算法的存儲空間大小固定,和輸入規模沒有直接的關係時,空間複雜度記做O(1)。舉例以下:
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
複製代碼
當算法分配的空間是一個線性的集合(如數組),而且集合大小和 輸入規模n成正比時,空間複雜度記做O(n)。
舉例以下:
void fun(int n){
int[] array = new int[n];
//....
}
複製代碼
注意:遞歸也屬於線性空間
當算法分配的空間是一個二維數組集合,而且集合的長度和寬度都 與輸入規模n成正比時,空間複雜度記做O(n2)。舉例以下:
void fun(int n){
int[][] array = new int[n][n];
//....
}
複製代碼
咱們之因此花大力氣去評估一個算法的時間複雜度和空間複雜度,其根本緣由是由於計算機的速度和空間是有限。時間複雜度和空間複雜度常常是矛盾的。若是須要下降算法的時間複雜度,則一般須要增長算法的空間複雜度;若是想減小額外空間的使用,則一般會致使算法的時間複雜度增長。 所以咱們須要在它們兩個之間做一個取捨,大多數狀況下咱們會選擇犧牲空間複雜度而成全時間複雜度,由於程序運行期間時間是沒法回收的,而空間是能夠回收的。
參考:《漫畫算法》