算法字面意思,計算方法;算法
算法規定了求解給定類型問題所需的全部處理步驟
以及執行順序
,使得問題能在有限時間內機械的求解,一個算法就是對特定問題求解步驟的一種描述,再具體一點,算法是一段有窮的指令序列;算法必須能使用某種語言描述;數組
例如:函數
計算1到5的和 ,這個需求,如何來實現,第一步作什麼,第二步作什麼,整個計算步驟和執行順序統稱爲算法,若是最終可以在有限的步驟下求出正確的和,那這就是一個合格的算法;spa
有窮性設計
算法必須在執行有窮步後結束code
肯定性io
算法的每個步驟都必須是明肯定義的,class
可行性效率
算法中的每一步都是能夠經過已實現的操做來完成的變量
輸入
一個算法具有0或多個輸入
輸出
一個算法有一個或多個輸出,它們與輸入有着特定的關係
算法與程序的區別,算法只是一種描述,可使用任何語言,可是一般不能直接被計算機運行,而程序則是算法的具體實現,使用某種計算機語言;
正確性:對於合法的輸入產生符合要求的輸出
易讀性:算法應該儘量易讀,便於交流,這也是保證正確性的前提(註釋可提升易讀性)
健壯性:當輸入非法數據時,算法可做出適當反應而不至於崩潰(例如輸出錯誤緣由);
時空性:指的是算法的時間複雜度和空間複雜度,算法分析主要也是分析算法的時間複雜度和空間複雜的,其目的是提升算法的效率;
解決同一問題的算法可能有多種,咱們但願從中選出最優的算法,效率高的,佔用空間小的,爲此咱們就須要對算法進行評估和分析;
一般評估算法根據兩個度量
合理選擇一種或幾種操做做爲'標準操做',無特殊說明默認以賦值操做做爲標準操做;
肯定算法共執行多少次標準操做,並將這次數規定爲算法的計算量
以算法在全部時輸入下的計算量最大值做爲算法的最壞狀況時間複雜度
以算法在全部時輸入下的計算量最小值做爲算法的最好狀況時間複雜度
以算法在全部時輸入下的計算量平均值做爲算法的平均狀況時間複雜度
最壞和平均狀況時間複雜度統稱爲時間複雜度;
它們一般擁有相同的大O表示法
如:最壞:O(n) 平均O(n/2) 忽略係數後都爲O(N)
注意:時間複雜度一般以量級來衡量,也就是說不須要精確的計算到底執行了幾步,而是得出其計算量的數量級便可,並忽略常數,由於當數量級足夠大時,常數對於計算量的影響能夠忽略不計;
如: (n-1)(n-2) 數量級爲 n^2
時間複雜度使用大O表示,如O(1)
1.
void aFunction(){ int c = 10 + 20; int d = c * c; printf(d); }
上列算法若以賦值運算做爲標準操做,則該算法的計算量爲2,其時間複雜度記爲O(1),爲何是O(1)呢,是由於2是一個常數,常數對於函數的增加影響並不大,因此計算量爲常數時表示爲O(1),按照這種方式,即便計算量爲2000,一樣記爲O(1),稱爲常數階
2.
void bFunction(int n){ for(int i = 0;i < n;i++){ // n int c = 2 * i;// 1 int d = 3 * i;// 2 } }
此時函數的循環次數由未知數n來決定,循環體內計算量爲2,當n是一個天然數時,函數的計算量等於(n)(2),此時時間複雜度爲O(n),不管用常數2
對n進行加減乘除對於n的指數都沒有影響,當n足夠大時,內部的2次計算量能夠忽略,因此記爲O(n),稱爲線性階
更粗陋的度量方法是函數體包含一層循環時記爲O(n)
3.
void bFunction(int n){ for(int i = 0;i < n;i++){ for(int j = 0;j < i;j++){ } } }
外層循環次數爲n,內層循環次數隨着n的增加而增加且最大值爲n-1次,那麼整個函數的計算量爲(n)(n-1),常數能夠忽略,因此時間複雜度記爲O(n^2) ,稱爲平方階
粗陋的方法是有兩層嵌套循環,且循環次數都隨着n的增加而增加,因此是O(n^2),以此類推,可是要注意下面這種狀況
4.
void bFunction(int n){ for(int i = 0;i < n;i++){ for(int j = 0;j < 3;j++){ } } }
此時內層循環的循環次數是固定(常數)因此不會影響計算量的數量級,時間複雜度記爲O(n)
5.
void bFunction(int n){ for(int i = 3;i < n;){ i *= 3; } }
此時函循環次數會隨着3循環體中的常數3的的變化而變化,咱們能夠用對數來表示,
假設循環次數爲s,循環條件可表示爲 s = 3^s < n;(即i自己爲3,其次冪會不斷的增加,但結果要小於n)
固然這裏有個條件是i的初值必須和每次乘等的值相同,造成次冪的增加;
用對數表示爲s = log3n,時間複雜度記爲O(log3n),常數能夠忽略,因此最後是O(logn)稱之爲對數階
對數階的數量級小於線性階,由於若n的值相同,對數階計算量必然小於線性階
6.
void bFunction(int n){ for(int i = 0;i < n;i++){ for(int j = 0;j < n;j++){ for(int k = 0;k < n;k++){ } } } }
上述算法時間複雜度爲O(n^3),3爲常數,對應着循環的嵌套層數;也能夠用O(n^C)表示,稱爲多項式階
7.
void bFunction(int n){ int num = 2; for(int i = 0;i < n;){ //O(n) num *= 2; } for (int j = 0;j<num;j++){ //O(n) } }
上述函數輸入的參數n將做爲循環次數的指數,假設循環次數爲s, s = 2^n,那麼時間複雜度爲O(2^n),
稱之爲指數階,可記爲O(C^n)
8.
void bFunction(int n) { for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ } } for(int i=0;i<n;i++){ } }
對於順序運行的算法,總時間複雜度等於算法中最大時間複雜度,即O(n^2)
9.
void bFunction(int n) { if(n % 2 ==0){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ } } }else{ for(int i=0;i<n;i++){ } } }
對於具有分支結構的算法,總時間複雜度等於算法中時間複雜度最大路徑的複雜度,即O(n^2)
常數 < 對數階 < 線性階 < 平方階 < 多項式階 < 指數階
O(1) < O(logn) < O(n) < O(n^2) < O(n^C) < O(C^n)
通常狀況下,一個算法的時間複雜度是算法輸入規模的函數;
一般認爲,具備指數階數量級的算法是實際不可計算的,而量級低於平方階的算法是高效的;
空間複雜度是對一個算法在執行過程當中臨時佔用存儲空間大小的度量;
一個算法在執行期間所須要的存儲空間包括如下部分:
** 估算算法空間複雜度時,通常值分析輔助變量所佔用的空間;
程序代碼佔用空間是固定的,一般比較小 輸入數據佔用空間一樣較小
強調:不管是時間複雜度仍是空間複雜度都用大O表示,表示方法也是相同的,不須要具體到幾回,只須要求出最大的數量級便可,一樣忽略係數,即複雜度對於常數的加減乘除是忽略不計的;
下列是兩個算法,用於對數組元素順序進行逆轉操做;
void f1(int a[],int n){ int i,temp; for(i = 0;i < n/2-1;i++){ temp = a[i]; a[i] = a[n-1-i]; a[n-1-i] = temp; } }
上述算法時間複雜度爲O(n);空間複雜度爲O(1);
解析:該算法員工定義了兩個整型的輔助變量,i和temp,輔助變量的個數與輸入數據沒有關係,是固定的常量C,對於擁有常數階複雜度的算法,其複雜度用O(1)表示;
void f2(int a[],int n){ int i,b[n]; for(i = 0;i < n;i++){ b[i] = a[n-i-1]; } for(i = 0;i < n;i++){ a[i] = b[i]; } }
上述算法時間複雜度爲O(n);空間複雜度爲O(1);
解析:
對於空間複雜度,其須要的計算量是(n)(2),大O表示爲O(n);
對於空間複雜度,一樣是兩個變量,可是數組b的空間大小是隨着輸入數據增加的,因此總體佔用爲 1 + n,大O表示爲O(n);
能夠發現空間複雜度相比時間複雜度更好度量,由於只須要根據定義的變量數量來計算便可,而時間複雜度會隨循環語句而變化;