大o表示法描述复杂度_时间复杂度,空间复杂度和大O表示法

大o表示法描述复杂度

This is the first post in my series Data Structures & Algorithms. As a boot camp grad, I found that once I started my professional career in software development, there was a gap in my fundamentals knowledge. Although I am not reversing a binary tree day-in-and-day-out, I do think it is important to learn these fundamentals simply because you will be a better developer by knowing they exist. This week I start things off by discussing Time and Space Complexity, and how you can use Big O notation to determine these metrics.

这是我的系列数据结构和算法中的第一篇文章。 作为一个新兵训练营的毕业生,我发现一旦我开始了软件开发的职业生涯,我的基础知识就会出现空白。 尽管我没有日复一日地逆转二叉树,但我确实认为学习这些基础知识很重要,因为您将因为知道它们的存在而成为更好的开发人员。 本周,我将从讨论时间和空间复杂度以及如何使用大O表示法确定这些指标开始。

时间复杂度 (Time Complexity)

A measurement of computing time that an algorithm takes to complete

一种算法完成计算时间的度量

What causes time complexity?

是什么造成时间复杂性?

  • Operations (+, -, *, /)

    操作( +-*/ )

  • Comparisons (>, <, ==)

    比较( ><== )

  • Looping (for, while)

    循环播放( forwhile )

  • Outside function calls (function())

    外部函数调用( function() )

大O符号 (Big O Notation)

The language and metric we use for talking about how long it takes for an algorithm to run

我们用于讨论算法运行需要多长时间的语言和指标

O(1)恒定时间 (O(1) Constant Time)

Not bound by the size of an input, only one operation is performed

不受输入大小的限制,仅执行一项操作

  • Direct query of data you are looking for

    直接查询您要查找的数据
  • No iterating (loops) are involved

    不涉及迭代(循环)

If you know the precise location of data you want to pull out of an Object {} or Array [], you can query for that item without having to iterate or perform any additional computation.

如果您知道要从Object {}或Array []提取数据的精确位置,则可以查询该项目,而不必进行迭代或执行任何其他计算。

Most of the time, if you’re using Constant Time, you are in good shape from a performance standpoint.

在大多数情况下,如果您使用的是Constant Time ,那么从性能的角度来看,您的状态会很好。

Let me show you an example in which I perform tasks that evaluate to Constant Time:

让我向您展示一个示例,在该示例中,我执行评估为“ 恒定时间”的任务:

First, I use the const keyword to declare a new variable with the identifier jedi and give this variable a collection of string values

首先,我使用const关键字声明一个标识符为jedi的新变量,并为该变量提供string值的集合

Next, I use the function keyword to create a new function and give it the identifier findAJedi. This function will have a single parameter with an identifier of jediList

接下来,我使用function关键字创建一个新函数,并为其赋予标识符findAJedi 。 该函数将具有一个标识符为jediList的单个参数

Using bracket notation [] I pull out the entry that is in index position 1

使用括号符号[]拔出索引位置1的条目

Since we already know where the data we want is, and we do not have to loop to get there, this operation is O(1) or Constant Time

因为我们已经知道我们想要的数据在哪里,并且我们不必循环到达那里,所以此操作为O(1)恒定时间

We call the findAJedi function with the variable jediList as the single argument and our findAJedi function prints anakin. He is the chosen one, right?

我们称findAJedi与可变功能jediList作为单独的参数和我们findAJedi功能打印anakin 。 他是被选中的人,对吗?

O(n)线性时间 (O(n) Linear Time)

Bound by the input, time increases linearly as input increases

受输入的约束,时间随着输入的增加而线性增加

  • Involves iteration to find a value (for orwhile loops)

    涉及迭代以查找值( forwhile循环)

Let me show you an example of an operation that evaluates to O(n) or Linear Time:

让我向您展示一个计算结果为O(n)线性时间的示例:

First, we use the const keyword to create a new variable with the identifier jedi that is assigned the value of an Array. We use the fill() method to populate this Array with five luke values that are of type string

首先,我们使用const关键字创建一个标识符为jedi的新变量,该变量被分配了Array的值。 我们使用fill()方法用五个类型为string luke值填充此Array

Next, we use the function keyword to create a new function with an identifier findLuke. This function will have a single parameter with an identifier of jediList

接下来,我们使用function关键字创建一个带有findLuke标识符的新函数。 该函数将具有一个标识符为jediList的单个参数

function findLuke(jediList) {

Inside of our findLuke function use the for keyword to create a for loop. We iterate through our jediList and use bracket notation [] to compare each entry to luke, when we find a match we console.log it

在我们的findLuke函数内部,使用for关键字创建一个for循环。 我们遍历jediList并使用方括号[]将每个条目与luke进行比较,找到匹配项后,我们进行console.log

for (let i = 0; i < jediList.length; i++) {
  if (jediList[i] === "luke") {
    console.log("found luke")
  }
}

Since we are iterating through the entire Array, our Big O would be O(n). Right now our jediList only has five entries, but what if we had 10,000, or 1,000,000,000? These are good considerations to think about as you write code.

由于我们要遍历整个Array ,因此我们的Big O将为O(n) 。 现在,我们的jediList只有五个条目,但是如果我们有10,000或1,000,000,000,该怎么办? 这些是编写代码时要考虑的好考虑因素。

We call our findLuke function that takes a single argument jedi and since all of our entries are luke, we console.log luke five times

我们调用带单个参数jedi findLuke函数,由于所有条目都是luke ,所以我们console.log luke五次

findLuke(jedi)
// found luke
// found luke
// found luke
// found luke
// found luke

O(n²)二次时间 (O(n²) Quadratic Time)

Often thought of as “worst case”, multiple nested iterations occur

通常被认为是“最坏的情况”,发生了多次嵌套迭代

  • Involves two nested loops

    涉及两个嵌套循环
  • Each item in two collections need to be compared to each other

    两个集合中的每个项目都需要相互比较

I am sure that you have been here before, I know I sure have. Nesting loops is never a good idea and there is a good reason for that. Speaking in terms of Big O, when you are iterating over a collection, and then iterating again inside of that first iteration that will produce a Big O of O(n²)

我敢肯定你以前来过这里,我知道我确实曾经来过。 嵌套循环从来都不是一个好主意,这是有充分理由的。 说到大O,当您遍历一个集合,然后在第一次迭代内部再次迭代时,将产生O(n²)的大O。

Let me show you an example of a function that produces a Big O of O(n²):

让我向您展示产生O(n²)的Big O的函数示例:

const jedi = ["mace windu", "yoda", "obi wan"];


function logJediDuos(jediList) {
  for (let i = 0; i < jediList.length; i++) {
    for (let j = 0; j < jediList.length; j++) {
      console.log(jediList[i], jediList[j]);
    }
  }
}


logJediDuos(jedi);

First, we use the const keyword to create a new variable with the identifier jedi that is assigned to an Array of three string values

首先,我们使用const关键字创建一个标识符为jedi的新变量,该变量分配给三个string值的Array

const jedi = ["mace windu", "yoda", "obi wan"]

Next, we use the function keyword to create a new function with an identifier of logJediDuos. This function has a single parameter jediList

接下来,我们使用function关键字创建一个具有logJediDuos标识符的新函数。 该函数具有单个参数jediList

function logJediDuos(jediList) {

Inside of logJediDuos we use the for keyword to create our first for loop. In our for statement we declare that we want to iterate through the length of jediList until that length is greater than the value of i. We increase the value of i after each iteration

logJediDuos内部,我们使用for关键字创建第一个for循环。 在for statement我们声明我们要迭代jediList的长度,直到该长度大于i的值。 每次迭代后我们增加i的值

for (let i = 0; i < jediList.length; i++) {

Inside of the previous for loop, we create another for loop. Inside of our for statement we make sure to give our index variable an identifier of j to ensure we do not mutate the state of our i variable.

在前面的for循环内部,我们创建了另一个for循环。 在for语句中,我们确保为索引变量赋予j的标识符,以确保我们不会使i变量的状态发生变化。

Using bracket notation [] we use our index variables i and j to console.log each pair inside of our jediList

使用括号符号[]我们使用索引变量ijconsole.logjediList内部的每一对

for (let i = 0; i < jediList.length; i++) {
  for (let j = 0; j < jediList.length; j++) {
    console.log(jediList[i], jediList[j])
  }
}

When we invoke our logJediDuos function we get this result:

当我们调用我们的logJediDuos函数时,我们得到以下结果:

logJediDuos(jedi)
// mace windu mace windu
// i = 0, j = 0
// mace windu yoda
// i = 0, j = 1
// mace windu obi wan
// i = 0, j = 2
// yoda mace windu
// i = 1, j = 0
// yoda yoda
// i = 1, j = 1
// yoda obi wan
// i = 1, j = 2
// obi wan mace windu
// i = 2, j = 0
// obi wan yoda
// i = 2, j = 1
// obi wan obi wan
// i = 2, j = 2

I am only covering a handful of common Big O times in this post. If you want to learn more about advanced Big O times you can do so by following the links provided below:

在这篇文章中,我仅介绍几个常见的大O时代。 如果您想了解有关高级大O时间的更多信息,可以通过下面的链接进行操作:

O(n!)阶乘时间 (O(n!) Factorial Time)

Adds a nested loop for every loop

为每个循环添加一个嵌套循环

Read more here

在这里阅读更多

O(log N)对数 (O(log N) Logarithmic)

Involves searching algorithms if sorted

如果排序,则涉及搜索算法

Read more here

在这里阅读更多

O(2 ^ N)指数 (O(2^N) Exponential)

Recursive algorithms that solve a problem of size N

解决大小为N的问题的递归算法

Read more here

在这里阅读更多

简化大O (Simplifying Big O)

  • Always assume worst-case scenario

    始终假设最坏的情况
  • Remove constants

    删除常量
  • Different terms for inputs

    输入的不同术语
  • Drop non-dominants

    掉落非优势

始终假设最坏的情况 (Always assume worst-case scenario)

It is a very common practice to iterate through a list of data in your program, and lists can vary greatly in size. When I say to always assume worst-case scenario I mean that in a few different ways.

遍历程序中的数据列表是一种非常普遍的做法,列表的大小可能相差很大。 当我说总是假设最坏的情况时,我的意思是说有几种不同的方式。

  • If you query for data, assume it is the last item in the list

    如果查询数据,则假定它是列表中的最后一项
  • Assume the list you’re iterating through will get bigger

    假设您要遍历的列表会更大
  • Assume some machines will run your algorithm slower than on your machine

    假设某些计算机的算法运行速度比计算机上的算法慢

删除常量 (Remove constants)

When we are determining the Big O of an algorithm it helps to remove repeated measurements (constants). This allows us to get a more clear read on the speed of the algorithm by removing unneeded calculation.

当我们确定算法的Big O时,它有助于去除重复的测量值(常数)。 这样,通过删除不需要的计算,我们可以更清楚地了解算法的速度。

Let me show you an example where we remove constants:

让我向您展示一个删除常量的示例:

function printJedi(jediList) {
  jediList.forEach((jedi) => {
    console.log(jedi)
  }
  // O(n)


  jediList.forEach((jedi) => {
    console.log(jedi)
  }
  // O(n)
}


printJedi(['anakin', 'obi wan', 'yoda'])


// O(n) + O(n) = O(2n)

First, we create a new function with the identifier printJedi, this function has a single parameter (jediList)

首先,我们使用标识符printJedi创建一个新function ,该函数具有单个参数( jediList )

function printJedi(jediList) {

Inside of our printJedi function we call the forEach() method on jediList two separate times

我们的内部printJedi功能我们称之为forEach()的方法jediList两个独立的次

jediList.forEach((jedi) => {
  console.log(jedi)
}
// O(n)


jediList.forEach((jedi) => {
  console.log(jedi)
}
// O(n)

Since we are iterating through the entire jediList array, each operation is O(n). At the end of our function, we add up our Big O (O(n) + O(n)) which results in O(2n). We can simplify this by removing the constants which in this case is 2. After this, we are left with Big O of O(n).

由于我们要遍历整个jediList数组,因此每个操作都是O(n) 。 在函数的最后,我们将Big O( O(n) + O(n) )相加,得出O(2n) 。 我们可以通过删除在这种情况下为2 的常量来简化此过程。 之后,我们剩下O(n) Big O。

输入的不同术语 (Different terms for inputs)

In cases that you iterate through different pieces of data, the Big O calculation will reflect that. Since each collection of data will most likely be different sizes, the consideration of its time complexity comes into play.

如果您遍历不同的数据,Big O计算将反映出这一点。 由于每个数据集合很可能具有不同的大小,因此需要考虑其时间复杂性。

Let me show you an example of calculating Big O while using multiple collections of data:

让我向您展示一个使用多个数据集合计算Big O的示例:

function printJediAndSith(jediList, sithList) {
  jediList.forEach(jedi => console.log(jedi))


  sithList.forEach(sith => console.log(sith))
}


printJediAndSith(["anakin", "obi wan"], ["vader", "sidious"])


// O(a + b)

Above, we create a new function with the identifier printJediAndSith, this function has two parameters: jediList and sithList

上面,我们用标识符printJediAndSith创建了一个新function ,该函数有两个参数: jediListsithList

function printJediAndSith(jediList, sithList) {

Inside of printJediAndSith we call the forEach() method on the jediList array and the sithList array

printJediAndSith内部,我们在jediList数组和sithList数组上调用forEach()方法

jediList.forEach(jedi => console.log(jedi))


sithList.forEach(sith => console.log(sith))

Now, what do you think the Big O is of the printJediAndSith function? Since we iterate through a collection of data it should be O(n), right? Not in this case.

现在,您认为printJediAndSith函数的大O是什么? 由于我们遍历数据集合,因此应该为O(n) ,对吗? 在这种情况下不行。

Remember, these parameters will likely have different lengths. It is because of this that we determine the Big O of printJediAndSith to be O(a + b).

请记住,这些参数的长度可能不同。 因此,我们确定printJediAndSith的Big O为O(a + b)

掉落非优势 (Drop non-dominants)

Inside of functions a lot of different things can happen. This includes the range of time complexity as well. When determining the Big O of an algorithm, for the sake of simplifying, it is common practice to drop non-dominants. In short, this means to remove or drop any smaller time complexity items from your Big O calculation.

在函数内部,可能发生许多不同的事情。 这也包括时间复杂度的范围。 在确定算法的Big O时,为了简化起见,通常的做法是舍弃非主要对象 。 简而言之,这意味着从Big O计算中删除或删除任何较小的时间复杂性项。

Let me show you an example of dropping non-dominants:

让我向您展示删除非主要对象的示例:

function printAndSumJediAttendance(jediList) {
  jediList.forEach(list => console.log(list))


  jediList.forEach(firstList => {
    jediList.forEach(secondList => {
      console.log(firstList + secondList)
    })
  })
}


printAndSumJediAttendance([1983, 66, 1138, 94, 1977])

First, we create a new function with the identifier printAndSumJediAttendance, this function has a single parameter jediList

首先,我们创建了一个新的function与标识符printAndSumJediAttendance ,这个函数有一个参数jediList

function printAndSumJediAttendance(jediList) {

Inside of printAndSumJediAttendance we call the forEach() method on the jediList parameter. Because we are iterating through a collection of data this Big O evaluates to O(n).

printAndSumJediAttendance内部,我们在jediList参数上调用forEach()方法。 因为我们正在遍历数据集合,所以这个大O求值为O(n)

jediList.forEach(list => console.log(list))

On the next line, we call the forEach() method on our jediList parameter. Inside of this forEach block, we call forEach on jediList again. Because we are iterating through nested loops, our Big O evaluates to O(n²)

在下一行,我们在jediList参数上调用forEach()方法。 在此forEach块内部,我们再次在jediList上调用forEach 。 因为我们要遍历嵌套循环,所以我们的大O求值为O(n²)

jediList.forEach(firstList => {
  jediList.forEach(secondList => {
    console.log(firstList + secondList)
  })
})

Let me break this Big O calculation down a bit:

让我将这个大O的计算分解一下:

function printAndSumJediAttendance(jediList) {
  // O(n)
  jediList.forEach(list => console.log(list))


  // O(n^2)
  jediList.forEach(firstList => {
    jediList.forEach(secondList => {
      console.log(firstList + secondList)
    })
  })
}
// O(n + n^2) -> simplified -> O(n^2)

As you can see, if we add up the Big O calculations from this function, we are left with a result of O(n + n²).

如您所见,如果我们将这个函数的Big O计算结果相加,则会得到O(n + n²)

If we analyze this, we see that the part of our calculation with the largest Big O is — because of this, we drop the n. We do this because is more _dominant_ than n. Once we have refactored our calculation, we are left with this result: O(n²).

如果我们对此进行分析,我们会发现计算中具有最大Big O的部分为因此,我们将n丢弃。 我们这样做是因为不止_dominant_ n 。 重构计算后,将得到以下结果: O(n²)

空间复杂度 (Space Complexity)

Parallel to time complexity, space complexity is the measurement of memory (space) that an algorithm needs

与时间复杂度并行的是,空间复杂度是算法需要的内存(空间)的度量

是什么导致空间复杂性? (What causes Space Complexity?)

  • Variables

    变数
  • Data structures

    数据结构
  • Function calls

    函数调用
  • Allocations

    分配

Let me show you an example of how we would calculate the space complexity:

让我向您展示如何计算空间复杂度的示例:

function buildALightsaber(pieces) {
  let totalPieces = 0 // O(1)
  totalPieces = 4 // O(1)


  for (let i = 0; i < pieces.length; i++) {
    // O(n)
    addCrystals() // O(n)
    const hasTheForce = true // O(n)
    totalPieces++ // O(n)
  }
  return totalPieces // O(1)
}


// O(3 + 4n) -> simplified -> O(n)

First, we create a new function with the identifier buildALightsaber that has a single parameter pieces

首先,我们创建了一个新的function与标识符buildALightsaber有一个参数pieces

function buildALightsaber(pieces) {

Inside of buildALightsaber, we use the let keyword to create a new variable with the identifier totalPieces that is assigned to the value 0. On the following line, we reassign the variable totalPieces to the value of 4

buildALightsaber内部,我们使用let关键字创建一个标识符为totalPieces的新变量, totalPieces其分配给值0 。 在下一行,我们将变量totalPieces重新分配为值4

Creating and assigning values to variables is O(n) (constant time); therefore, these two steps are both O(1)

为变量创建和分配值的时间为O(n) (恒定时间); 因此,这两个步骤都是O(1)

let totalPieces = 0; <-- // O(1)
totalPieces = 4; <-- // O(1)

Next, we create a for loop and iterate through pieces

接下来,我们创建一个for循环并遍历各个pieces

Since we are going to be iterating through a collection of data, the Big O of this operation will evaluate to O(n)

由于我们要遍历数据集合,因此此操作的Big O将评估为O(n)

for (let i = 0; i < pieces.length; i++) { <-- // O(n)

Inside of our for loop, we call a function with an identifier addCrystals(). Next, we use the const keyword to create a variable with the identifier hasTheForce and assign it the value true. Last, we increment our totalPieces by one.

for循环内部,我们调用一个带有标识符addCrystals()的函数。 接下来,我们使用const关键字创建一个标识符为hasTheForce的变量,并将其赋值为true 。 最后,我们将totalPieces加1。

In terms of evaluating space complexity while calling functions, creating variables, and updating the values of variables inside of an iteration (for or while loops), you have to be mindful of the fact that these actions will occur for each iteration. It is because of this that all actions mentioned will be O(n)

关于在调用函数,创建变量以及在迭代内部( forwhile循环)中更新变量的值时评估空间复杂性,您必须注意以下事实:每次迭代都会发生这些操作。 因此, 所有提到的动作都是O(n)

addCrystals(); <-- // O(n)
const hasTheForce = true; <-- // O(n)
totalPieces++; <-- // O(n)

After we finish iterating through pieces we return the value of totalPieces

完成对pieces迭代之后,我们返回totalPieces的值

Since this is a single action, the Big O is evaluated to O(1) or constant time

由于这是单个操作,因此将大O评估为O(1)恒定时间

return totalPieces; <-- // O(1)

If we calculate the Big O of this function we originally get (3 + 4n). After we apply our principles of simplifying Big O, we know that we can remove constants which will make our final result O(n)

如果我们计算此函数的Big O,则最初得到(3 + 4n) 。 应用简化Big O的原理后,我们知道可以删除常数 ,从而使最终结果为O(n)

综上所述 (In Summary)

I hope after reading this you have a solidified idea of how time and space complexity work, what their importance is in the functions/algorithms we write, and how we can calculate these complexities using Big O notation.

我希望阅读完这篇文章后,您对时间和空间复杂度的工作方式,它们在我们编写的函数/算法中的重要性以及如何使用Big O表示法计算这些复杂度的想法有一个扎实的认识。

Next week I will begin to take a deep dive into arguably the most popular data structure JavaScript developers use, the Array. See you then!

下周,我将开始深入探讨可以说JavaScript开发人员使用的最流行的数据结构Array。 回头见!

翻译自: https://medium.com/dev-genius/time-complexity-space-complexity-and-big-o-notation-500d6104f727

大o表示法描述复杂度

你可能感兴趣的:(数据结构,leetcode,python,算法,机器学习)