算法是對特定問題求解步驟的一種描述,它是指令的有限序列,其中每條指令表示一個或多個操作。
程序=數據結構+算法
從求n個數的和出發,評價算法的好壞
#include <stdio.h>
int sum1(int n)
{
return (1+n)*n/2;
}
int sum2(int n)
{
int res=0;
for(int i=0;i<=n;i++){
res+=i;
}
return res;
}
int main()
{
int n=5;
printf("%d\n",sum2(n));
}
在力扣中運行代碼,看執行時間哪一個比較少
輸入n=3時(第一個是sum2函數,第二個是sum1)
輸入n=10000時(第一個是sum2函數,第二個是sum1)
事後統計法
比較不同算法對同一組輸入的執行處理時間
缺點:
執行時間嚴重依賴硬件以及運行時各種不確定的環境因素
必須編寫相應的測算代碼
測試數據的選擇比較難保證公正性
一般從以下維度來評估算法的優劣:正確性、可讀性、健壯性(對不合理輸入的反應能力和處理能力)
時間複雜度(time complexity):估算程序指令的執行次數(執行時間)
大O表示法
一般用大O表示法來描述複雜度,它表示的是數據規模 n 對應的複雜度。
忽略常數、係數、低階;對數階一般省略底數:O(log2n) 、O(log9n) 統稱爲 O(logn)
兩個原則
a)多項相加,保留最高階的項。且係數變爲1。
b)多項相乘,乘積之後的數再進行大O,f*g=O(f)O(g)=O(fg)。
時間複雜度的估算
順序執行的代碼只會影響常數項,可忽略。只需要關注循環中的基本操作,分析執行次數與n的關係。
1.循環裏i初始化1次,i<4的判斷執行了4次,i++也是4次,輸出操作也是4次
2.
3.第一個for循環裏面的語句一共執行了n次,這取決於i;裏面還有個for循環,通過上面一題知道單個for循環執行了1+3n 次。
4.n能除多少個2就能執行幾次
5.第一個for循環,i要乘以幾個2才能到n,就是執行次數(2的幾次方是n,那麼當然是log以2爲底n的對數)。
for循環第二項和第三項執行次數都是相同的。
第二個for循環,很熟悉,1+3n次
6.
斐波那契數列的時間複雜度
public class Main {
/*
* 0 1 1 2 3 5 8 13 21……
*/
//第一種遞歸方法的時間複雜度:函數被調用了幾次就執行了幾次
public static int fib1(int n )
{
if(n<=1)
return n;
return fib(n-1)+fib(n-2);
}
public static int fib2(int n )
{
if(n<=1)
return n;
int first=0;
int second=1;
for (int i = 0; i < n-1; i++) {
int sum=first+second;
//兩兩相加,下一組的第一個數就是上一組的第二個數
//下一組的第二個數就是原來上一組的和
first=second;
second=sum;
}
return second;
}
public static void main(String[] args) {
System.out.print(fib2(70));
}
}
分析
第一種遞歸方法fib1的時間複雜度:函數被調用了幾次就執行了幾次。
可以看到,有很多重複調用,當n=5時,先調用兩次自身,再調用4次,最後調用8次。
所以複雜度爲O(2^n)
n=6時也是一樣,上下兩層都是2倍關係
而第二種非遞歸方法fib2的時間複雜度是O(n)
數學公式求解數列
空間複雜度(space complexity):估算所需佔用的存儲空間
由於現在硬件發展的較好,一般情況下我們更側重於時間複雜度。
程序在執行時,除了需要存儲空間來存放本身所用的指令、常數、變量、和輸入數據外,還需要一些對數據進行操作的工作單元和存儲一些爲實現計算所需信息的輔助空間