天天看點

趣學算法--入門

    • 算法的特性
    • 時間複雜度
    • 空間複雜度

算法的特性

1. 有窮性:由若幹條指令組成的有窮序列,總是在執行若幹次後結束,不可能永不停止
2. 确定性:每條語句有确定的含義,無歧義
3. 可行性:在目前環境條件下可以通過有限運算實作
4. 輸入輸出:有零個或多個輸入,一個或多個輸出
           

“好”算法的标注如下

1. 正确性:滿足具體問題的需求,程式運作正常無文法錯誤,能通過軟體測試,達到預期的需求

2. 易讀性:辨別符命名規則,簡潔易懂,注釋語句恰當适量,友善自己和他人閱讀,便于後期調試和修改

3. 健壯性:對于非法資料及操作有較好的反應和處理

4. 高效性:指算法運作效率高,即算法運作所消耗的時間短。是以我們将算法基本運算的執行次數作為時間複雜度的衡量标準

5. 低存儲性:低存儲性是指算法所需要的存儲空間低。是以算法占用的控件大小稱為空間複雜度

時間複雜度

定義:算法運作需要的時間,一般将算法的執行次數作為時間複雜度的度量标準

//算法1_1
sum=;    //運作1次    
total=;  //運作1次
for(i=;i<=n;i++){    //運作n次
    sum+=i;           //運作n次
    for(j=;j<=m;j++){    //運作n*n次,嵌套循環    
        total+=i*j;       //運作n*n次
    }
}

T(n)=++n+n+n*n+n*n
           
趣學算法--入門

備注:運作次數中有高次方時,一般以最高次方來計算算法的運作時間

趣學算法--入門
趣學算法--入門

備注:在算法分析中,漸近複雜度是對算法運作次數的粗略估計,大緻反映問題規模增長趨勢,而不必精确計算算法的運作時間,在計算漸近時間複雜度時,可以隻考慮對算法運作時間貢獻最大的語句,而忽略那些運算次數少的語句。循環語句中處在循環内層的語句往往運作次數最多

不是每個算法都能直接計算運作次數

如下算法1_2

findx(int x){
foor(int i=;i<n;i++){    //在a[n]數組中順序查找x
    if(a[i]==x)
        return i;    //傳回其下标i
    }
    return -;
}
           

此類型算法我們就很難計算其到底執行了多少次,因為這依賴于i在資料中的位置,是以我們隻能計算其平均執行次數為(n+1)/2

有些算法,如排序,查找,插入等算法,可以分為最好,最壞和平均情況分别求算法漸近複雜度,但是我們考察一個算法通常考察最壞的情況,而不是最好的情況

空間複雜度

定義:算法占用的空間大小,一般将算法的輔助空間作為衡量空間複雜度的标準,主要包括三部分

1. 輸入/輸出資料

2. 算法本身

3. 額外需要的輔助空間

算法1_3

//将兩個數交換,并分析其空間複雜度
swap(int x,int y){    //x與y交換
    int temp;
    temp=x;    //此時temp為輔助空間
    x=y;        
    y=temp;        
} 
           
趣學算法--入門

該算法使用了一個輔助空間temp,空間複雜度為O(1)

注意:遞歸算法中,每一次遞推需要一個棧空間來儲存調用記錄,是以空間複雜度需要計算遞歸棧的輔助空間

算法1_4

fac(int n){
        if(n<){    //小于零的數無階乘值
            printf("n<0,data error!");
            return -;
        }else if(n== || n==){
            return ;
        }else{
            return n*fac(n-);
        }
    }
           

遞推與回歸過程如下圖:

趣學算法--入門

此時,計算機内部是使用一種稱為“棧”的資料結構,其特性為不允許中間插入或抽取,是以稱為“後進先出”

趣學算法--入門

因為劃分棧空間的數量取決于n的值,是以此算法的空間複雜度為O(n),時間複雜度也為O(n)

趣學算法--入門
趣學算法--入門

n爆炸增量問題

一棋盤麥子故事告訴我們,當高次方到達一定程度,會出現爆炸性增長,如2的64次方達到了将近7730億,假如算法的時間複雜度達到了O(2^n)會怎麼樣?

常見的算法時間複雜度有一下幾類

1. 常數階:常數階的算法運作的次數是一個常數

2. 多項式階:很多算法時間複雜度是多項式,通常用O(n),O(n^2)表示

3. 指數階:指數階時間複雜度運作效率極差,O(2^n)使用這樣的算法要慎重

4. 對數階:對數階時間複雜度運作效率較高,常見的有O(logn),O(nlogn)

趣學算法--入門

從指數階增量随着x的增加而急劇增加,而對數階增加緩慢,他們之間的關系為:

O(1)

Fib1(int n){
        if(n<){
            return -;
        } if(n== || n==){
            return ;
            return Fib1(n-)+Fib1(n-);
        }
    }

//算法複雜度
n=時,T(n)=;
n=時,T(n)=;
n=時,T(n)=; //調用Fib1(2),Fib(1)和執行一次加法運算Fib1(2)+Fib1(1)
           
趣學算法--入門

算法改進,算法1_6

既然斐波那契數列中的每一項是前兩項之和,如果記錄前兩項的值,隻需要一次加法運算就可以得到目前項,我們可以用數組

Fib2(int n){
        if(n<){
            return -;
        }
        int *a=new int[n];
        a[]=;
        a[]=;
        for(int i=;i<=n;i++){
            a[i]=a[i-]+a[i-];
        }
        return a[n];
    }
           

很明顯,時間複雜度為O(n)

此算法使用了一個輔助數組記錄中間結果,空間複雜度也為O(n),其實我們隻需要得到第n個斐波那契數,直接結果隻是為了下一次使用,根本不用記錄下來,是以我們可以采用疊代法進行算法設計

算法1_7

Fib3(int n){
        int i,s1,s2;
        if(n<){
            return -;
        }if(n== || n==){
            return ;
        }
        s1=;
        s2=;
        for(i=;i<=n;i++){
            s2=s1+s2; //輾轉相加法
            s1=s2-s1;//記錄前一項
        }
        return s2;
    }
           

疊代過程如下:

趣學算法--入門

此算法使用了若幹個輔助變量,疊代輾轉相加,每次記錄前一項,時間複雜度為O(n),但空間複雜度降到O(1)

我們還能繼續降階麼?可以,最多可以降到對數階O(logn)

繼續閱讀