大O表示法

103 阅读2分钟

通常用大O表示法表示算法的效率

如下图,从左到右,从上到下,效率依次递增

image.png

下面看几个算法题对应的时间空间复杂度

  • 时间复杂度:O(1);空间复杂度:O(1)

    时间的消耗和数组长度无关,无论数组多长,时间消耗都是固定的

    function(array){
        return array[0]
    }
    
  • 时间复杂度:O(log n);空间复杂度:O(1)

    // 二分法找数
    const arr = [1,2,3,4,5,6,7];
    const fn = (target)=>{
         let start = 0,end=arr.length-1,middle,ele;
         while(start<=end){
             middle = Math.floor((start+end)/2);
             ele = arr[middle];
             if(ele === target){
                 console.log(middle);
                 return middle
             } else if(target < ele){
                 end = middle-1
             } else {
                 start = middle+1
             }
         }
     }
     fn(3)
    
    • 每次查找都会把查找区间缩小一半(start/end/middle)。

    • 所以查找次数最多为 log₂(n) 次(n 为数组长度)。

    • 详细解释

      • 第一次查找:n 个元素
      • 第二次查找:n/2 个元素
      • 第三次查找:n/4 个元素
      • · · ·
      • 直到只剩 1 个元素

      所以总共查找次数为 log₂(n) 次,时间复杂度为 O(log n); 因为是循环实现,没有递归调用栈,空间复杂度为 O(1)

  • 时间复杂度:O(n²); 空间复杂度:O(1)

    //选择排序
    const arr = [1, 2, 19, 12, 21, 13, 8, 4];
    
    const fn = (arr) => {
      for (let i = 0; i < arr.length; i++) {
          let min = i;
          for (let j = i + 1; j < arr.length; j++) {
              if (arr[j] < arr[min]) {
                  min = j;
              }
          }
          [arr[i], arr[min]] = [arr[min], arr[i]];
      }
      return arr;
    };
    const res = fn(arr);    
    
    • 外层循环遍历数组每个元素 i

    • 内层循环从 i+1 开始,找出最小值索引 min

    • 将当前索引 i 和最小值位置 min 的元素交换。

    • 详细解释

      • 时间复杂度
        • 外层循环:执行 n 次(n 是数组长度)

        • 里层循环

          • 第一次内层循环:比较 n-1 次
          • 第二次内层循环:比较 n-2 次
          • · · ·
          • 最后一次:比较 1 次

          所以总共查找次数为(n-1)+(n-2)+(n-3)+...次,时间复杂度为 O(n²)。

      • 空间复杂度
        • 只使用了常数级额外空间(几个变量 ijmin)。

          所以空间复杂度为:O(1)

  • 时间复杂度:O(2ⁿ);空间复杂度O(n)

    //斐波拉契数列
    function fn(num) {
        if(num<=2) return 1;
        return fn(num-1)+fn(num-2);
    }
    

image.png

可以看到,每次调用都会分出两个子调用,形成一棵二叉树结构

  • 每层节点数量为 2d2^d,其中 d 是深度。

  • 总调用次数 ≈ 2n2^n

    所以时间复杂度为O(2ⁿ)

递归的空间复杂度取决于最大递归调用栈的深度

  • 最深路径是从 fn(n) → fn(n-1) → fn(n-2) → ... → fn(1)

  • 所以最大栈深度是 n

    所以空间复杂度为O(n)