算法复杂度分析个人思考

174 阅读2分钟

时间复杂度

大 O 时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度。

// 示例代码
function example(n) {
  let i = 0;

  while (i <= n) {
    i = i * 2;
  }
}

/*
时间复杂度:
代码的执行时间T(n)=O(f(n))

首先找出这段代码中和n有关系的代码是第6行,因为代码的执行时间T(n)与代码的执行次数N成正比,
所以如果能知道第6行代码执行了多少次,就知道了执行时间与数量规模n的关系。
变量i每次增大2倍,循环在i>n时结束,所以可以列出不等式 2^N <= n 求得N<=log(n),即T(n)=O(log(n)),
所以这段代码的时间复杂度是O(log(n))

空间复杂度:
因为变量不随数据规模n变化,变量为常数。所以空间复杂度为O(1)
 */

/*
总结规律
1、找出和数据规模相关的代码,如果没有就是常量级复杂度O(1)
2、找出代码终止的条件,列出执行次数的求解函数
3、执行时间T(n)与执行次数N成正比,所以求出N即可知道时间复杂度
 */

/*
递归函数一般是2^n
循环嵌套一般是n^2或者m*n
常见的复杂度为O(1),O(n),O(n^2),O(m*n),O(log n),O(2^n),O(n*log n),O(n^1/2)开根号。
当数据规模n足够大时,忽略常系数等影响,最终应该转化为其中一个
 */

常见复杂度

空间复杂度

空间复杂度表示算法占用存储空间随数据规模变化的关系 空间复杂度计算比较简单,常用的为O(1),O(n),O(n^2)。

空间复杂度指的是除了除了原本必须要用的存储空间外,运行算法需要的额外存储空间。

比如需要实现一个栈,无论使用数组还是链表都需要一个长度为n的默认存储集合。这个存储空间就不能计算进入空间复杂度。

再比如判断是否是回文字符串的算法

  • 如果使用双指针的算法,则空间复杂度为O(1),因为执行这个算法仅仅需要存储几个变量,而与数据规模n无关。
  • 如果将字符串分解为数组再反转数组合并为字符串的方式,则空间复杂度为O(n),因为执行这个算法需要额外创建一个长度为n的数组

最好、最坏、平均、均摊时间复杂度

最好最坏很好理解,分别表示代码片段在最理想和最糟糕情况下的时间复杂度。平均时间复杂度表示根据各种可能情况出现的概率求得的加权平均复杂度。 均摊时间复杂度是一种特殊的平均复杂度,适用范围比较小。比如下面这段代码,大部分时间复杂度都很低O(1),个别情况复杂度较高O(n),并且是连续反复出现的。这时可以将这一组操作合并起来分析,将复杂度较高的操作平均分摊到复杂度较低的操作上。均摊时间复杂度一般等于最好情况复杂度

// 全局变量,大小为10的数组array,长度len,下标i。
int array[] = new int[10]; 
int len = 10;
int i = 0;

// 往数组中添加一个元素
void add(int element) {
   if (i >= len) { // 数组空间不够了
     // 重新申请一个2倍大小的数组空间
     int new_array[] = new int[len*2];
     // 把原来array数组中的数据依次copy到new_array
     for (int j = 0; j < len; ++j) {
       new_array[j] = array[j];
     }
     // new_array复制给array,array现在大小就是2倍len了
     array = new_array;
     len = 2 * len;
   }
   // 将element放到下标为i的位置,下标i加一
   array[i] = element;
   ++i;
}