数据结构补课 - 算法复杂度分析

为什么要学数据结构和算法:

解决问题方法的效率,和数据的组织方式有关:

例子:如何在书架上摆放图书?

题眼:
操作一:新书怎么插入?
操作二:怎么找到某本指定的书?

方法一:随便放
操作一:哪里有空放哪里
操作二:找书就比较头疼了

方法二:按照书名的拼音字母顺序放
操作一:需要挨个位置移动
操作二:二分查找

方法三:把书架按类别分区,每个类别内按照字母顺序放
操作一:搜索类别,二分查找确定位置,挪出空位
操作二:搜索类别,二分查找确定位置
思考:空间怎么分配,类别怎么分配

解决问题方法的效率,和空间的利用效率有关:

例子:写方法,顺序打印从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;
}

结果:这两种方法在分别多次运行累加的时间,方法二比方法一快一个数量级

 

数据结构和算法的概念:

//什么是数据结构
数据对象:数据在计算机中的组织方式,包括逻辑结构和物理存储结构。
数据对象集的相关操作:数据对象必定与一系列加在其上的操作相关,完成这些操作所用的方法就是算法。
数据结构就描述了数据对象集及与其相关的操作集。
//什么是算法 算法是一个有限的指令集,接收一些输入,在有限步骤之后终止,产生输出。 算法的每一条指令都要有明确的目标、在计算机处理范围内、不依赖于任何一种语言及具体实现手段。

 

算法分析:

//空间复杂度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) ……

首先明确:常数项对时间增长影响不大,所以被忽略

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)

posted @ 2018-10-30 15:27  月亮和电池  阅读(210)  评论(0编辑  收藏  举报