Алгоритмы и структуры данных на PHP: с чего начать новичку
Освоение алгоритмов и структур данных — ключевой этап в становлении любого разработчика, включая тех, кто выбрал PHP. Многие новички ошибочно полагают, что для веб-разработки достаточно знать только синтаксис и фреймворки. Однако понимание того, как эффективно организовывать данные и обрабатывать их, напрямую влияет на производительность и масштабируемость ваших проектов. В этой статье мы разберём базовые концепции на практических примерах PHP, которые помогут вам сделать первый шаг в мир эффективного программирования.
Почему структуры данных важны в PHP?
PHP — язык с динамической типизацией, и его встроенный массив (array) на самом деле является гибридом: он может работать и как список, и как словарь (хеш-таблица). Это мощно, но без понимания лежащих в основе структур вы рискуете писать неоптимальный код. Например, поиск элемента в неотсортированном массиве из 10 000 записей может занять в 100 раз больше времени, чем поиск в хеш-таблице.
Основные структуры, которые мы рассмотрим:
- Массивы — основа основ, но с нюансами.
- Стек и очередь — для задач с определённым порядком обработки.
- Связные списки — для динамического управления памятью.
- Хеш-таблицы — для быстрого доступа по ключу.
1. Массивы и работа с ними: от простого к сложному
В PHP массив — это упорядоченное отображение, которое связывает ключи со значениями. Но даже с этой универсальной структурой можно работать по-разному.
Индексированные и ассоциативные массивы
Индексированный массив использует целочисленные ключи, а ассоциативный — строковые. Пример:
<?php
// Индексированный массив
$fruits = ["apple", "banana", "cherry"];
echo $fruits[1]; // banana
// Ассоциативный массив (хеш-таблица)
$user = [
"name" => "Alice",
"age" => 30,
"city" => "Moscow"
];
echo $user["name"]; // Alice
?>
Операции с массивами
Добавление, удаление и поиск — базовые операции. Важно знать их временную сложность:
array_push()/$arr[] = $val— O(1) в среднем.array_pop()— O(1).array_shift()— O(n), так как переиндексирует все элементы.in_array()— O(n) для неотсортированного массива.isset($arr[$key])— O(1) для ассоциативных массивов.
Пример эффективного использования ассоциативного массива для подсчёта частоты слов:
<?php
$text = "apple banana apple cherry banana apple";
$words = explode(" ", $text);
$freq = [];
foreach ($words as $word) {
if (isset($freq[$word])) {
$freq[$word]++;
} else {
$freq[$word] = 1;
}
}
print_r($freq);
// Array ( [apple] => 3 [banana] => 2 [cherry] => 1 )
?>
2. Стек и очередь: принципы LIFO и FIFO в PHP
Стек (Last In, First Out) и очередь (First In, First Out) — классические структуры, которые легко реализовать на PHP с помощью массивов.
Реализация стека
Стек используется, например, для проверки парности скобок или отмены действий (undo).
<?php
class Stack {
private array $items = [];
public function push($item): void {
$this->items[] = $item; // O(1)
}
public function pop() {
return array_pop($this->items); // O(1)
}
public function peek() {
return end($this->items); // O(1)
}
public function isEmpty(): bool {
return empty($this->items);
}
}
// Пример: проверка сбалансированности скобок
function isBalanced(string $expr): bool {
$stack = new Stack();
$pairs = [')' => '(', ']' => '[', '}' => '{'];
for ($i = 0; $i < strlen($expr); $i++) {
$char = $expr[$i];
if (in_array($char, ['(', '[', '{'])) {
$stack->push($char);
} elseif (isset($pairs[$char])) {
if ($stack->isEmpty() || $stack->pop() !== $pairs[$char]) {
return false;
}
}
}
return $stack->isEmpty();
}
echo isBalanced("({[]})") ? "true" : "false"; // true
echo isBalanced("({[})") ? "true" : "false"; // false
?>
Реализация очереди
Очередь пригодится для обработки задач в порядке поступления (например, очередь сообщений).
<?php
class Queue {
private array $items = [];
public function enqueue($item): void {
$this->items[] = $item; // O(1)
}
public function dequeue() {
return array_shift($this->items); // O(n) — переиндексация
}
public function peek() {
return $this->items[0] ?? null; // O(1)
}
public function isEmpty(): bool {
return empty($this->items);
}
}
// Пример: симуляция очереди печати
$printQueue = new Queue();
$printQueue->enqueue("Document1.pdf");
$printQueue->enqueue("Photo.jpg");
$printQueue->enqueue("Report.docx");
while (!$printQueue->isEmpty()) {
echo "Printing: " . $printQueue->dequeue() . "\n";
}
// Printing: Document1.pdf
// Printing: Photo.jpg
// Printing: Report.docx
?>
Важно: array_shift() работает за O(n). Для более производительной очереди используйте SplQueue (встроенный класс PHP) или реализуйте очередь на основе связного списка.
3. Сортировка и поиск: алгоритмы, которые должен знать каждый
Сорти