使用大O符号来提高应用程序的性能

160 阅读4分钟

用户体验在现代软件中是至关重要的,而性能对于良好的体验是至关重要的。现代软件都是关于性能的,它可以决定你吸引和保留用户的能力。在设计时考虑到性能的应用程序,与那些没有考虑到性能的应用程序相比,有更大的成功机会。

一个常见的误解是,一段简单的代码不会造成任何伤害。相反,你应该始终假定添加一段代码的后果可能比你想象的还要糟糕。反过来说,只需要几行代码就可以显著提高你的应用程序的性能。

在本指南中,我们将探讨提高现代应用程序性能的最简单的方法之一:使用大O符号法来衡量你的代码的复杂性。

什么是大O符号法?

大O符号法是一个描述算法复杂性的数学过程。这是计算机科学领域的一个非常重要的概念,它描述了一个算法的复杂性将如何基于输入的大小而增长。

Graph Showing Operations VS Elements

图片来源:Huyen Pham

有两种方法可以衡量一个算法的复杂性。

  • 空间复杂度衡量的是一个算法根据输入的大小所需要的确切空间量。它基本上是通过计算算法中的变量所占用的空间来衡量的。
  • 时间复杂度衡量的是一个算法根据输入大小所需的确切时间量。它本质上取决于一个算法在完成执行之前需要执行多少个步骤。

我们可以通过测量运行一个算法需要多长时间来计算该算法的时间复杂性。在计算一个算法的复杂性时,我们要考虑到三种情况。

  • 最佳情况--当算法将在最快的时间内完成。这始终是最佳方案
  • 平均情况--当算法将在平均时间内完成时
  • 最坏的情况--当算法将在最慢的时间内完成。这始终是最差的解决方案

当使用大O符号测量算法的复杂性时,你应该总是考虑最坏的情况。大O符号中的 "O "代表函数的顺序,"n "代表输入的数量。

O(1)

一个算法的最佳时间复杂度是恒定时间,也被称为O(1)。具有恒定时间的算法将总是需要相同的时间来执行。这种算法的执行与输入的大小无关。

想象一下,我们有一个函数,返回一个数字的平方。

const returnSquare = (num) => num * num;

returnSquare 函数的执行时间将总是相同的。这就是恒定时间的工作原理,一个算法的运行时间是相同的,无论输入的大小如何。

现在,设想我们有一个接收数组的函数。我们想总是返回数组的第一个元素,无论数组的大小如何。

const getFirstItem = (arr) => arr[0];

getFirstItem 函数有一个恒定的时间复杂度,因为无论数组的大小如何增长,它都会以相同的时间运行。

O(n)

最常见的时间复杂度是线性时间复杂度,也被称为O(n)。

当一个算法的运行时间与输入的大小成线性变化时,该算法具有线性时间复杂度。

想象一下,我们有一个简单的数组,我们想遍历数组以找到一个特定的项目。

const searchItem = (arr, item) => {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === item) {
      return item;
    }
  }
}

在最好的情况下,我们要找的项目是第一个项目,我们不需要对整个数组进行映射。最坏的情况是,这一项可能是最后一项,我们需要遍历整个数组。

随着我们的数组的增长,这个算法的时间复杂性也在线性增长。每次我们在我们的算法上看到一个循环,我们就可以认为那段代码可以是一个线性时间复杂度的算法。

O(log n)

你可能在学校学习过对数。对数是一种数学运算,它决定了某个数字需要乘以多少倍才能达到另一个数字。

想象一下,我们有一个10个元素的数组,我们用一秒钟的时间来遍历这个数组。随着这个算法的时间复杂度的增加,我们将用两秒钟的时间遍历20个元素的数组,用三秒钟遍历30个元素的数组,以此类推。

O(log n)算法的一个好例子是二进制搜索。二进制搜索通过在每次迭代中把数组分成两半来找到一个特定元素在排序数组中的位置。

Binary Search Array

使用Excalidraw制作的图像。

在每个步骤中,该算法将问题的大小减少一半。以二进制搜索算法为例:每次迭代都要对数组进行分割,直到找到特定的项目。

O(n ^ 2)

当运行时间与输入大小的平方成正比时,一个算法的时间复杂度是二次的。

想象一下,我们有一个数组,对于每一个项目,我们想再次循环,比较当前的元素。

const findItem = (arr, newArr) => {
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < newArr.length; j++) {
      if (arr[i] === newArr[j]) {
        console.log('hello!');
      }
    }
  }
}

这是一个四次方时间复杂度算法的例子。嵌套循环导致时间复杂度增加一倍。每当我们的数组大小增加时,复杂度就会成四倍地增加。

O(n!)

O(n!)代表一个算法可能具有的最坏的时间复杂度。在写代码时,你不想写一段时间复杂度为O(n!)的代码,也被称为阶乘时间复杂度。

一个具有O(n!)时间复杂度的算法达到无穷大的速度比你想象的要快得多。在阶乘时间复杂度下,我们正在为每一个输入添加一个嵌套循环。

知道这是可能的,这很好,但你可能不想用这种时间复杂度来写代码。

结论

开发人员喜欢根据可读性来衡量代码的强度。把可读性作为一个基准没有错,但它不是你应该考虑的唯一基准。

性能在所有现代软件中都起着至关重要的作用,但编写高性能的代码并不总是那么简单。意识到你的代码库的复杂程度并避免创建不必要的东西是很重要的。

大O符号(Big O Notation)可以帮助你通过测量代码的复杂性来编写高性能的代码。这个概念已经存在了很多年,并继续帮助开发人员编写有吸引力的、可执行的软件。

The postUsing Big O notation to improve app performanceappeared first onLogRocket Blog.