算法概述和時間複雜度

什麼是算法

算法是用於解決特定問題的一系列的執行步驟,使用不同算法,解決同一個問題,效率可能相差非常大

比如:求第 n 個斐波那契數(fibonacci number)

/**
     * 斐波那契數列 Fibonacci sequence
     * 斐波那契數列(Fibonacci sequence),又稱黃金分割數列、
     * 因數學家萊昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖爲例子而引入,故又稱爲“兔子數列”,
     * 指的是這樣一個數列:0、1、1、2、3、5、8、13、21、34、……
     * 在數學上,斐波那契數列以如下被以遞推的方法定義:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
     */
    public static int fibonacciSequence_01(int i){
        if(i<=1){
            return i;
        }
        return fibonacciSequence_01(i-1)+fibonacciSequence_01(i-2);
    }

    public static int fibonacciSequence_02(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;
    }

如何評判一個算法的好壞?

如果單從執行效率上進行評估,可能會想到這麼一種方案 : 比較不同算法對同一組輸入的執行處理時間 .這種方案也叫做:事後統計法

此方案有比較明顯的缺點 1.執行時間嚴重依賴硬件以及運行時各種不確定的環境因素 2.必須編寫相應的測算代碼 3.測試數據的選擇比較難保證公正性

還可以從以下維度來評估算法的優劣

  • 正確性、可讀性、健壯性(對不合理輸入的反應能力和處理能力)
  • 時間複雜度(time complexity):估算程序指令的執行次數(執行時間)
  • 空間複雜度(space complexity):估算所需佔用的存儲空間

大O表示法(Big O)

一般用大O表示法來描述複雜度,它表示的是數據規模 n 對應的複雜度,此方法忽略常數、係數、和低階

◼ 注意:大O表示法僅僅是一種粗略的分析模型,是一種估算,能幫助我們短時間內瞭解一個算法的執行效率

對數階的細節

log2n = log29 ∗ log9n 

所以 log2n 、log9n 統稱爲 logn

常見的複雜度

下圖來自維基百科

 

 

常數級別O(1)

O(1):算法複雜度和問題規模無關。換句話說,哪怕你拿出幾個PB的數據,我也能一步到位找到答案。

理論上哈希表就是O(1)。因爲哈希表是通過哈希函數來映射的,所以拿到一個關鍵字,用哈希函數轉換一下,就可以直接從表中取出對應的值。和現存數據有多少毫無關係,故而每次執行該操作只需要恆定的時間

(當然,實際操作中存在衝突和衝突解決的機制,不能保證每次取值的時間是完全一樣的)

 

 

對數級別O(logN)

 

 O(logN):算法複雜度和問題規模是對數關係。換句話說,數據量大幅增加時,消耗時間/空間只有少量增加(比如,當數據量從2增加到2^64時,消耗時間/空間只增加64倍,常見於二分法

int number = 1; // 語句執行一次
while (number < n) { // 語句執行logn次
  // 這裏的2是log的底數
  // 底數在大O符號中是省去的
  number *= 2; // 語句執行logn次
}

線性級別O(N)

 

 

O(n):算法複雜度和問題規模是線性關係。換句話說,隨着樣本數量的增加,複雜度也隨之線性增加

int i =0; // 語句執行一次
while (i < n) { // 語句執行n次
  print(i); //語句執行n次
  i++; // 語句執行n次
}

線性對數級別O(NlogN)

 

 

public static void test6(int n) {
        // log5(n)
        // O(logn)
        while ((n = n / 5) > 0) {
            System.out.println("test");
        }
    }

平方級別O(N^2)

 

 O(n^2)計算的複雜度隨着樣本個數的平方數增長。這個例子在算法裏面,就是那一羣比較挫的排序,比如冒泡等等

for (int i = 0; i < n; i++) { // 語句執行n次
  for (int j = 0; j < n; j++) { // 語句執行n^2次
     print('I am here!'); // 語句執行n^2
  }
}

指數級別O(2^N)

如果一個算法的運行時間是指數級的(exponential),一般它很難在實踐中使用

斐波那契數的例子的第一個算法就是O(2^N)

 

 有時候算法之間的差距,往往比硬件方面的差距還要大

 

算法的優化方向

1.用盡量少的存儲空間 

2.用盡量少的執行步驟(執行時間) 

3.根據情況,可以 

  • 空間換時間 
  • 時間換空間
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章