數據結構補課 - 算法複雜度分析

爲何要學數據結構和算法:算法

解決問題方法的效率,和數據的組織方式有關:數據結構

例子:如何在書架上擺放圖書?

題眼:
操做一:新書怎麼插入?
操做二:怎麼找到某本指定的書?

方法一:隨便放
操做一:哪裏有空放哪裏
操做二:找書就比較頭疼了

方法二:按照書名的拼音字母順序放
操做一:須要挨個位置移動
操做二:二分查找

方法三:把書架按類別分區,每一個類別內按照字母順序放
操做一:搜索類別,二分查找肯定位置,挪出空位
操做二:搜索類別,二分查找肯定位置
思考:空間怎麼分配,類別怎麼分配

解決問題方法的效率,和空間的利用效率有關:數據結構和算法

例子:寫方法,順序打印從1到n的所有正整數

方法一:循環
function print(n){
  for(var i=0;i<n;i++){
    console.log(i);
  }
  return;
}
//數量級多大都能正常輸出

方法二:遞歸
function print(n){
  if(n){
    print(n-1);
    console.log(n);
  }
  return;
}
//數量級到必定程度直接崩潰,由於遞歸很是耗費空間

解決問題方法的效率,和算法的巧妙程度有關:函數

例子:i*x的i次方 ,i取值從0~9,在x=1.1時計算他們的和

方法一:老老實實翻譯成代碼
function compute(n,a,x){
  var result = a[0];
  for(var i=1;i<=n;i++){
    result += (a[i] * Math.pow(x,i));
  }
  return result;
}

方法二:提取公因數
function compute(n,a,x){
  var result = a[n];
  for(var i=n;i>0;i--){
    result = a[i-1] + x*result;
  }
  return result;
}

結果:這兩種方法在分別屢次運行累加的時間,方法二比方法一快一個數量級

 

數據結構和算法的概念:ui

//什麼是數據結構
數據對象:數據在計算機中的組織方式,包括邏輯結構和物理存儲結構。
數據對象集的相關操做:數據對象一定與一系列加在其上的操做相關,完成這些操做所用的方法就是算法。
數據結構就描述了數據對象集及與其相關的操做集。
//什麼是算法 算法是一個有限的指令集,接收一些輸入,在有限步驟以後終止,產生輸出。 算法的每一條指令都要有明確的目標、在計算機處理範圍內、不依賴於任何一種語言及具體實現手段。

 

算法分析:spa

//空間複雜度S(n)
根據算法寫成的程序在執行時佔用存儲單元的長度。
這個長度每每與輸入數據的規模有關,空間複雜度太高可能致使內存超限,形成程序非正常中斷。

//時間複雜度T(n)
根據算法寫成的程序在執行是耗費時間的長度。
這個長度一般與輸入數據的規模有關,時間複雜度太高的算法可能致使咱們有生之年都等不到運行結果。

 

時間複雜度的計算:翻譯

一般狀況下咱們關注:最壞狀況複雜度和平均複雜度。

//複雜度的漸進表示法(空間複雜度也同樣,這裏省略)
T(n) = O(f(n)) 表示算法時間複雜度的上界
T(n) = Ω(g(n))表示算法時間複雜度的下界
T(n) = θ(h(n))表示上界和下界同時適用

//時間複雜度函數優劣排序(從左到右,愈來愈慢)
1 < log n < n < n*log n < n^2 < n^3 < 2^n < n!

//幾個時間複雜度的計算要點
相加取大值:T1(n) + T2(n) = max(O(f1(n)),O(f2(n)))
相乘取乘積:T1(n) * T2(n) = O(f1(n) * f2(n))
n的k階多項式:T(n) = θ(n^k)
for循環:T(n) = 循環次數 * 循環體代碼的複雜度
if-else:T(n) = max(if判斷複雜度,if分支複雜度,else複雜度)

正常循環的時間複雜度:O(1)、O(n)、O(n^2) ……code

首先明確:常數項對時間增加影響不大,因此被忽略

function test1(){
  console.log("hello");
}
//T(n) = 1 → O(1)

function test2(n){
  for(var i=0;i<n;i++){
    console.log("hello");
  }
}
//T(n) = (n*1) → O(n)

function test3(n){
  for(var i=0;i<n;i++){
    for(var j=0;j<n;j++){
      console.log("hello");
    }
  }
}
//T(n) = (n*n*1) → O(n^2)

function test4(n){
  //第一部分 T1(n) = (n*n*1) → O(n^2)
  for(var i=0;i<n;i++){
    for(var j=0;j<n;j++){
      console.log("hello");
    }
  }
  //第二部分 T2(n) = (n*1) → O(n)
  for(var x=0;x<n;x++){
    console.log("world");
  }
}
//T(n) = max(T1(n^2),T2(n)) → O(n^2)

function test5(n){
  if(n>=0){
    //第一部分 T1(n) = (n*n*1) → O(n^2)
    for(var i=0;i<n;i++){
      for(var j=0;j<n;j++){
        console.log("hello");
      }
    }
  }else{
    //第二部分 T2(n) = (n*1) → O(n)
    for(var x=0;x<n;x++){
      console.log("world");
    }
  }
}
//T(n) = max(T1(n^2),T2(n)) → O(n^2)

二分法的時間複雜度:logn對象

//在有序數列中查找target,利用二分法實現
function test(arr,target){
  var left=0;
  var right = arr.length-1;
  while(left<=right){
    var middle = Math.floor((left + right)/2);
    if(arr[middle]>target){
      //中間值比target大,target在左邊,序列變動爲 left~middle-1
      right = middle-1;
    }else if(arr[middle]<target){
      //中間值比target小,target在右邊,序列變動爲 middle+1~right
      left = middle+1;
    }else{
      //不大不小就是找到了
      return middle;
    }
  }
  return -1;
}

//時間複雜度:
//n + n/2 + n/4 + …… + n/(2^k)
//令 n/(2^k) = 1,可得 k = log2n
//T(n) = O(logn)
相關文章
相關標籤/搜索