🧠 Основи алгоритмів і структур даних у JavaScript: повний гайд для початківців і не тільки. Детальний вступ до алгоритмів і структур даних для веброзробників на JavaScript з практичними прикладами, поясненням Big O нотації, масивами, об’єктами, списками, стеком, чергами та іншими фундаментальними поняттями.
🔍 Що таке алгоритми і структури даних?
Алгоритм — це набір покрокових інструкцій для досягнення конкретної мети або вирішення задачі.
Структура даних — це спосіб організації, зберігання й обробки даних так, щоб алгоритми могли працювати ефективно.
Без розуміння цих концепцій неможливо створювати продуктивні програми, проходити технічні співбесіди чи писати код, що масштабується.
Таким чином, алгоритми – це кроки по вирішенню задачі, структури даних – це способи роботи з даними для ефективної імплементації алгоритму.
🧮 Big O Notation — мова продуктивності алгоритмів
Big O — це математичний запис, що описує найгірший випадок роботи часу або пам’яті, які використовує алгоритм.
Ось нижче – структурована таблиця Big O — з максимально повним переліком найпоширеніших випадків роботи алгоритмів і колонкою їх ефективності від найефективніших до найповільніших:
📊 Таблиця Big O Notation: порівняння ефективності алгоритмів
| Big O | Назва (тип зростання) | Приклад / Опис | Ефективність |
|---|---|---|---|
| O(1) | Константна | Доступ до елемента в масиві arr[0] | 🟢 Найкраща |
| O(log n) | Логарифмічна | Бінарний пошук в відсортованому масиві | 🟢 Дуже хороша |
| O(√n) | Квадратний корінь | Алгоритми для перевірки простих чисел | 🟢 Добра |
| O(n) | Лінійна | Прохід по масиву, обробка кожного елемента | 🟡 Середня |
| O(n log n) | Лінійно-логарифмічна | Сортування (MergeSort, QuickSort у середньому) | 🟡 Прийнятна |
| O(n²) | Квадратична | Вкладені цикли: сортування бульбашкою, добутки матриць | 🔴 Повільна |
| O(n³) | Кубічна | 3 вкладені цикли, обрахунки в графах | 🔴 Дуже повільна |
| O(2ⁿ) | Експоненціальна | Генерація всіх підмножин, рекурсивний Фібоначчі | 🔴 Неефективна |
| O(n!) | Факторіальна | Перебір всіх перестановок (наприклад, задачі типу комівояжера) | 🔴 Катастрофічна |
💡 Пояснення ефективності:
- 🟢 Ефективні (O(1), O(log n)) — ідеальні для великих обсягів даних.
- 🟡 Середні (O(n), O(n log n)) — практично використовуються у більшості реальних алгоритмів.
- 🔴 Повільні (O(n²) і гірше) — прийнятні тільки для малих обсягів даних або як винятки.
📌 Приклади для розуміння:
- O(1):
const first = arr[0]; - O(n):
for (let i = 0; i < arr.length; i++) console.log(arr[i]); - O(n²):
for (let i = 0; i < arr.length; i++) { for (let j = 0; j < arr.length; j++) { console.log(arr[i], arr[j]); } } - O(2ⁿ):
function fib(n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); }
🔎 Знання Big O — це ключ до написання швидких, надійних та масштабованих алгоритмів. Завжди прагни уникати O(n²) і гірших складностей для великих обсягів даних.
Приклад O(n):
function printAll(arr) {
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
}
📦 Основні структури даних у JavaScript
1️⃣ Масиви (Array)
Масив — це впорядкована колекція елементів.
Приклади:
const fruits = ["apple", "banana", "cherry"];
console.log(fruits[1]); // "banana"
Типові методи:
push,popshift,unshiftslice,spliceforEach,map,filter,reduce
Задача: знайти найбільше число у масиві
function findMax(arr) {
return Math.max(...arr);
}
2️⃣ Об’єкти (Object) та Map
Об’єкти — це колекції пар “ключ-значення”, де ключі — рядки, а значенням може буть чим в JS.
Приклад об’єкту:
const user = { name: "John", age: 30 };
console.log(user["age"]); // 30
Map дозволяє використовувати будь-який тип ключа:
const map = new Map();
map.set("name", "Alice");
map.set(123, true);
console.log(map.get("name")); // Alice
3️⃣ Set (множина)
Set — колекція унікальних значень.
Приклад:
const numbers = new Set([1, 2, 2, 3]);
console.log(numbers); // Set {1, 2, 3}
4️⃣ Стек (Stack)
Принцип: LIFO — “останній прийшов — перший вийшов” – як млинці, що бабусі приготувала – останній млинчик зверху, але ти його з’їдаєш першим!
Приклад:
const stack = [];
stack.push(1);
stack.push(2);
console.log(stack.pop()); // 2
Застосування:
- історія сторінок у браузері
- зворотний обхід дерева
- парсер коду (перевірка дужок)
- стек в EventLoop
5️⃣ Черга (Queue)
Принцип: FIFO — “перший прийшов — перший вийшов” – це як черга в крамниці: хто перший зайняв чергу, того першого і обслужили і він пішов по своїх справах, потім – наступний покупець, а так далі, поки черга не скінчиться.
Реалізація:
const queue = [];
queue.push(1);
queue.push(2);
console.log(queue.shift()); // 1
Застосування:
- асинхронні події
- обробка запитів у черзі
6️⃣ Зв’язаний список (Linked List)
У JavaScript немає вбудованого типу, але його можна реалізувати:
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
const node1 = new Node(10);
const node2 = new Node(20);
node1.next = node2;
Перевага: швидке додавання/видалення елементів
Недолік: повільний доступ до елементів
Черги використовують часто, коли треба щось швидко додати або видалити, і коли не дуже часто потрібен доступ до елементів.
7️⃣ Графи та дерева (основи)
- Дерева: ієрархічна структура (DOM, файлові системи)
- Графи: вузли з довільними зв’язками (соцмережі, маршрути)
const tree = {
value: 1,
children: [
{ value: 2 },
{ value: 3 }
]
};
🔄 Часті шаблони алгоритмів (підходи)
| Назва шаблону | Суть |
|---|---|
| Two Pointers | Два індекси, що сходяться/розходяться |
| Sliding Window | Підмасиви фіксованого/динамічного розміру |
| Divide and Conquer | Розділити задачу на частини (наприклад, Merge Sort) |
| Recursion | Виклик функції зменшеної версії себе |
| Hashing / Frequency | Зберігання частот у Map/Object |
| Backtracking | Пошук з поверненням (лабіринт, Sudoku, комбінації) |
🧪 Приклад практичної задачі: Підрахунок входжень символів
function countChars(str) {
const map = {};
for (let char of str) {
map[char] = (map[char] || 0) + 1;
}
return map;
}
console.log(countChars("hello")); // {h: 1, e: 1, l: 2, o: 1}
🔧 Інструменти для практики
- 💻 Playcode.io — онлайн-редактор для JS
- 💪 LeetCode, Codewars — задачі на алгоритми
- 📘 Grokking Algorithms (наочна книга)
- 📺 YouTube канали: Techsith, WilliamFiset, freeCodeCamp
🧩 Висновок
Знання алгоритмів і структур даних у JavaScript — це ключ до створення швидкого, надійного та масштабованого коду. Розуміння Big O, ефективне використання масивів, об’єктів, стеку, черг і інших структур допомагає вирішувати складні задачі й готуватись до співбесід.
Навички алгоритмічного мислення — це не просто теорія, а практичний інструмент кожного розробника.
Якщо ти веброзробник або готуєшся до технічного інтерв’ю — ця база зробить тебе на голову вищим серед конкурентів.
Нижче Ви знайдете детальне пояснення ролі методів .map(), .filter() і .reduce() в алгоритмах на JavaScript — з практичними прикладами та фокусом на ефективність, продуктивність і чистоту коду.
🔍 Методи масивів .map(), .filter(), .reduce() і їх роль в алгоритмах
📌 Що спільного?
Ці методи — не мутують оригінальний масив і забезпечують декларативний, чистий, функціональний стиль коду.
Їх часто використовують у сучасних алгоритмах обробки даних, наприклад:
- трансформація даних (
.map) - фільтрація (
.filter) - акумуляція або агрегація (
.reduce)
🔹 .map() — трансформація даних
Метод створює новий масив, у якому кожен елемент обробляється функцією.
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6]
🔧 Алгоритмічна роль:
- Преобразування масиву без зміни довжини
- Часто використовується в препроцесингу даних (наприклад, API → UI)
🧠 Big O:
- Часова складність: O(n)
- Пам’ять: O(n) (створює новий масив)
🔹 .filter() — фільтрація елементів
Повертає новий масив, який містить тільки ті елементи, що задовольняють умову.
const numbers = [1, 2, 3, 4, 5];
const even = numbers.filter(n => n % 2 === 0);
console.log(even); // [2, 4]
🔧 Алгоритмічна роль:
- Використовується у фільтрації даних, наприклад:
- Пошук елементів
- Валідація
- Вилучення непотрібного
🧠 Big O:
- Часова складність: O(n)
- Пам’ять: O(k), де k — кількість відібраних елементів
🔹 .reduce() — агрегація / акумуляція
Повертає одне значення: число, обʼєкт, масив тощо.
Застосовується для зведення всього масиву до одного результату.
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 10
🔧 Алгоритмічна роль:
- Використовується в:
- Обчисленні сум, добутків, середніх
- Створенні словників/мап
- Групуванні даних (
groupBy)
🧩 Приклад: частотний аналіз символів
const str = "hello";
const freq = str.split('').reduce((acc, char) => {
acc[char] = (acc[char] || 0) + 1;
return acc;
}, {});
console.log(freq); // {h:1, e:1, l:2, o:1}
🧠 Big O:
- Часова складність: O(n)
- Пам’ять: залежить від типу результату
🤝 Комбінація методів
Комбінування .filter() + .map() + .reduce() — це потужна техніка в алгоритмах:
Приклад: сума квадратів парних чисел
const arr = [1, 2, 3, 4, 5];
const result = arr
.filter(n => n % 2 === 0)
.map(n => n ** 2)
.reduce((acc, n) => acc + n, 0);
console.log(result); // 20 (4 + 16)
📈 Коли використовувати:
| Метод | Коли використовувати |
|---|---|
.map() | Потрібно перетворити кожен елемент |
.filter() | Потрібно відфільтрувати частину елементів |
.reduce() | Потрібно звести все до одного значення/структури |
🚀 Переваги використання в алгоритмах:
- 🧼 Чистий код — читається легко, схоже на “людську мову”
- 🔁 Ланцюг викликів — легко комбінуються
- 🔒 Іммутативність — не змінюють оригінальні дані
- 📊 Візуальна зрозумілість — одразу видно, що саме відбувається
🔥 Альтернатива — цикл for?
Так, іноді цикл for або for...of швидший (менше накладних витрат), особливо для великих масивів або в критично важливому коді.
Але для більшості задач .map(), .filter(), .reduce() = кращий вибір за стиль, безпеку й читабельність.
🧩 Висновок:
Методи .map(), .filter() і .reduce() — це ключові інструменти сучасного JavaScript-розробника. Їх часто застосовують у реальних алгоритмах, при роботі з API, у компонентах React, при вирішенні задач на співбесідах.
Нижче приклади функцій для пошуку мінімального та максимального значення в масиві, а також аналіз їх часової складності (Big O).
🔧 Пошук min у масиві
function findMin(arr) {
if (arr.length === 0) return undefined;
let min = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
return min;
}
🧠 Аналіз:
- Часова складність:
O(n)— бо перевіряємо кожен елемент один раз. - Просторова складність:
O(1)— використовуємо лише одну зміннуmin.
🔧 Пошук max у масиві
function findMax(arr) {
if (arr.length === 0) return undefined;
let max = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
🧠 Аналіз:
- Часова складність:
O(n) - Просторова складність:
O(1)
🧪 Приклад виклику
const numbers = [5, 3, 9, 1, 7];
console.log(findMin(numbers)); // 1
console.log(findMax(numbers)); // 9
⏱ Альтернатива з Math.min / Math.max
const min = Math.min(...numbers); // O(n)
const max = Math.max(...numbers); // O(n)
❗️ Але
...(spread-оператор) створює копію масиву в пам’яті, тому для дуже великих масивів це може бути неефективно по пам’яті (O(n)).
✅ Висновок:
| Метод | Часова складність | Просторова складність | Переваги |
|---|---|---|---|
for цикл | O(n) | O(1) | Контроль, ефективність |
Math.min() | O(n) | O(n) через spread | Коротко, але гірше по пам’яті |