Нотация "О"

*. В чем идея "Big O Notation"?

*. Существует 10–20 различных способов решения проблемы. Используя нотацию Big O, мы можем найти лучший подход к решению проблемы.

ex: напишите функцию для вычисления суммы всех чисел от 1 до n.

sol: есть два основных подхода к этому.

Приложение1: функция addUpTo (n) {
let sum = 0;
for (let i = 0; i ‹n; i ++) {
sum + = i ;
}
возвратная сумма
}

Приложение2: функция addUpTo (n) {
return (n * (n - 1) / 2);
}

В нотации Big O Notation мы подсчитываем «количество простых операций», которые компьютер должен выполнить.

В подходе 2 у нас есть 3 операции (*, -, /), поэтому для любого значения «n» эти 3 операции останутся постоянными.

В подходе 1 мы имеем следующее:

‹I› сумма = 0; один раз

‹Ii› i = 0 тоже будет один раз,

‹Iii› i ‹n && i ++ операция будет выполнена n раз, т.е. 3n

‹Iv› + = (сложение n присваиваний) n раз, т.е. 2n

результат: ›i (const) + ii (const) + 3 * n + 2 * n =› (5n + 2).

************** Углубленное введение в нотацию Big O *************

Он говорит нам, «как время выполнения алгоритма растет по мере увеличения« размера входных данных ».

По сути, это взаимосвязь между «размером ввода» и «временем относительно» этого ввода.

По сути, это связь между тем, как изменяется размер ввода и время, необходимое для выполнения набора операций над вводом.

Математическое представление: O (f (n)).

Согласно вышеприведенному представлению, «n - размер ввода», а «f (n) - время, необходимое для выполнения алгоритма для этого размера ввода».

Существует три основных варианта O (f (n)):
*. f (n) может быть линейным, т.е. f (n) = n.

*. f (n) может быть квадратичным, т.е. f (n) = n * n.

*. f (n) может быть постоянным, т.е. f (n) = 1.

Ниже приведены несколько примеров для представления сценариев:

*. Линейный регистр:

function countUpAndDown () {

// при изменении значения n изменяются и выполняемые простые операции.

for (let i = 1; i ‹= n; i ++) {

console.log (я)

} // n почему? - цикл for продолжается до n раз

for (пусть j = n; j ‹= 1; j -) {

console.log (j)

} // n почему? - цикл for продолжается до n раз в обратном порядке

/*

следовательно, n + n = 2n, но мы игнорируем константу, поскольку для бесконечности
константа не имеет значения.

*/

}

ПРАВИЛА ПАЛЬЦА:
- - - - - -

Правило 1. Константа не имеет значения, если взять бесконечность.

O(2n) = O(n)

O(500) = O(1)

O(13n*n) = O(n*n)

O(n+10) = O(n)

O(1000n + 50) = O(n)

O(n*n + 5n + 8) => n*n + 5n => n(n+5) = O(n*n).

O(n*n + n*n*n) => (n*n(n + 1)) = O(n*n*n)

*. Когда мы увеличиваем график между f (n) («временная сложность») и n («размер ввода») до бесконечности, постоянное значение становится незначительным и игнорирующим.
*. Арифметические операции, присвоение переменных и доступ к элементам в массиве / объекте постоянны.

ПРОСТРАНСТВО-СЛОЖНОСТЬ

Сколько дополнительной памяти нам нужно выделить, чтобы запустить код в нашем алгоритме?

Сложность вспомогательного пространства:
Это относится к пространству, требуемому алгоритмом, не включая пространство, занимаемое входными данными.

ПРАВИЛА ПАЛЬЦА:

1. Большинство примитивных типов данных - это постоянное пространство.
2. Строке требуется пространство O (n), где n - длина строки.
3. Типы ссылок обычно O (n), где n - длина строки. длина массива n в случае объекта - количество ключей.

Пример: массив 1 имеет длину 4 n Массив 2 имеет длину 2, в результате чего массив 1 будет занимать пространство, вдвое превышающее массив 2.

Пример 1: Определите сложность пространства?

функция sum (arr) {
let total = 0;
for (let i = 0; i ‹arr.length; i ++) {
total + = arr [i]; // примитивный тип данных
}
return total;
}
sum ([1,2,3,4])

Наблюдение: независимо от длины массива, пространство будет занято только сразу, следовательно, сложность пространства будет постоянной.

ex2: найти космическую сложность?

функция double (arr) {
let nwArr = [];
for (let i = 0; i ‹arr.length; i ++) {
nwArr.push (arr [i]);
}
return nwArr;
}

Наблюдение: размер массива будет увеличиваться в соответствии с размером входного массива, следовательно, O (N).

пример 3: функция onlyElementsAtEvenIndex () {
let array = [1,2,3,4,5,6],
nwArr = [ ];
for (let i = 0; i ‹array.length; i ++) {
if (arr [i]% 2 == 0) {
nwArr.push (arr [i ]);
}
}
return nwArr;
}

ЛОГАРИФМЫ

По сути, это обратное экспоненте, так же как умножение и деление - пара.

Логарифмы и экспоненты являются парными.

*. Пример: log2 (8) = 3; // log2 (значение) = показатель степени → умножение 2 ‹exponent› на значение равно значению.
*. Что это означает: Это означает, что степень двойки равна 8, что, очевидно, будет равно 3.
*. Логарифмы всегда работают с основанием 2.

Примечание. Логарифм числа примерно определяет, сколько раз мы можем разделить это число на 2, прежде чем получим значение, меньшее или равное 1.

Ниже показано соотношение между временем O (N) и количеством элементов (N)

ПОИСК ОБЪЕКТОВ / МАССИВ ЧЕРЕЗ BIG O

let инструктор = {
‘firstName’: ‘Kelly’,
‘isInstructor’: true,
‘favNbrs’: [1,2,3,4]
};

# Ниже приведены действия с объектом, порядок которых не важен:
1. Вставка: O (1) // добавление нового свойства.
2 . Удаление: O (1) // удаление свойства.
3. Поиск: O (N)
4. Доступ: O (1) - console.log (инструктор ['isInstructor'])

# Большое количество объектных методов

1. Object.keys () - O (N)
2. Object.values ​​() - O (N)
3. Object.entries () - O (N)
4. .hasOwnProperty () - O (1), поскольку это похоже на доступ к свойству.

# массивов

1. Доступ к значениям - O (1)

В случае доступа к конкретному значению мы знаем точное местоположение / индекс, в результате временная сложность остается O (1) независимо от длины массива.

2. Поиск - O (N)

В случае поиска мы не знаем точного индекса, в котором находится конкретное значение, в результате мы можем перейти к последнему значению индекса в массиве.

3. Вставка:

В массив мы выполняем вставку двумя способами: push и pop.
Push: мы добавляем значение в последний индекс, поэтому Большой O будет O (1).

Pop: мы удаляем непосредственно из последнего значения индекса, поэтому большой O будет O (1).

Сдвиг: мы удаляем значение из первого индекса, в результате индекс остального значения повторно сдвигается до последнего элемента массива, поэтому Большой O будет O (N).

Unshift: мы добавляем значение к первой позиции индекса массива, что приводит к повторной индексации массива, поэтому большой O будет O (N).

Concat: это в основном объединение 2 или более массивов или просто вставка элементов одного массива в другой, это зависит от итерации массива, в результате БОЛЬШОЙ O будет O ( N).

Slice / Splice: в этих методах итерационная часть входит в ролевую игру и пропорциональна длине массива, поэтому Big O будет O (N).

Сортировка: большая буква O будет равна O (N * logN).

forEach / map / filter / reduce: Big O будет O (N), потому что итерация равна длине массива.

Вкратце: в случае массива и объектов, когда мы знаем индекс или ключ объекта для доступа к значению, в этом случае это O (1), но когда нам нужно выполнить итерацию для поиска значения как в случае массива, так и в случае объекта Big O будет O (N).

Примечание. Объекты быстро справляются практически со всем, но нет порядка, а массивы отлично подходят, когда нам нужен порядок.

ВВЕДЕНИЕ В РЕШЕНИЕ ПРОБЛЕМ

*. Что такое алгоритм?
*. Это набор шагов для выполнения определенной задачи.

*. Разберитесь в проблеме
*. Изучите конкретные примеры
*. Разбейте
*. Решить и упростить
*. Повторный цикл и рефакторинг

  1. Понимание проблемы
    A. Могу я переформулировать проблему своими словами?
    B. Что входит в проблему?
    C. Каков результат решения проблемы? D. Достаточно ли у меня информации для решения проблемы?
    E. Как мне пометить важные фрагменты данных, которые являются частью проблемы?

Ex1: напишите функцию, которая принимает 2 числа и возвращает их сумму.
Подход:
A. Нам нужно реализовать дополнительные функции.
B. Тип входных данных: число / Вводимое количество: два числа
C. Тип возвращаемых данных - число
D. Что нужно сфокусировать по типу входных данных: Пусто, Тип данных, Недействительный, Смешанный правильный и неправильный.

Ex2: напишите функцию, которая принимает строку и возвращает количество каждого символа в строке.
Подход:
A. Необходимо найти символы в строке.
Б. Ниже приведены исходные данные, которые используются для решения проблемы. (Анализ)
*. «Мой номер телефона 182763» (Нужно ли учитывать пробелы или цифры?)
*. ‘Hello Li’ (нужно указать прописными буквами или нет)
*. Пустые / смешанные / недопустимые типы данных.

C. Разбейте его на части:
Разбейте все решение проблемы на шаги.
Какие шаги мы будем выполнять, чтобы выполнить нашу задачу .
*. Сначала нам нужно выполнить базовую функциональность / логику.
*. Во-вторых, мы должны попробовать уровень проверки / тип ввода.

# Есть несколько разных подходов / шаблонов для написания кода:

  1. Счетчик частоты
  2. Множественный указатель
  3. Скользящее окно
  4. Разделяй и властвуй
  5. Динамическое программирование
  6. Жадные алгоритмы
  7. Обратное отслеживание

В этой статье мы подробно рассмотрим все вышеперечисленные пункты один за другим с чертовски большим количеством примеров:

  1. Счетчик частоты // БЕСПЛАТНО