0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Алгоритмы - это хорошо, но структуры данных - еще лучше.</p>
1
<p>Алгоритмы - это хорошо, но структуры данных - еще лучше.</p>
2
<p><strong>Структура данных</strong>- это конкретный способ хранения и организации данных. Иногда удобно организовать данные одним образом, иногда - другим. Здесь все зависит от решаемых задач.</p>
2
<p><strong>Структура данных</strong>- это конкретный способ хранения и организации данных. Иногда удобно организовать данные одним образом, иногда - другим. Здесь все зависит от решаемых задач.</p>
3
<p>Одну структуру данных мы уже изучили - это массив. По своей структуре массив - это совокупность элементов, к которым есть индексированный доступ (доступ по индексу).</p>
3
<p>Одну структуру данных мы уже изучили - это массив. По своей структуре массив - это совокупность элементов, к которым есть индексированный доступ (доступ по индексу).</p>
4
<p>С точки зрения хранения все работает немного сложнее. Массивы бывают разные, и внутри языка они реализуются тоже<a>по-разному</a>.</p>
4
<p>С точки зрения хранения все работает немного сложнее. Массивы бывают разные, и внутри языка они реализуются тоже<a>по-разному</a>.</p>
5
<p>Каноническая организация хранения массива - это непрерывный блок памяти. В таком случае индекс играет роль смещения по ней. Именно поэтому индексация в массивах начинается с нуля - индекс указывает на начало этого блока, а индекс под номером 1 уже считается смещением. Сложность в том, что в PHP нет настоящих массивов. Об этом вы узнаете в следующем курсе.</p>
5
<p>Каноническая организация хранения массива - это непрерывный блок памяти. В таком случае индекс играет роль смещения по ней. Именно поэтому индексация в массивах начинается с нуля - индекс указывает на начало этого блока, а индекс под номером 1 уже считается смещением. Сложность в том, что в PHP нет настоящих массивов. Об этом вы узнаете в следующем курсе.</p>
6
<p>Кроме массивов существует множество других структур данных: списки, хеш-таблицы, деревья, графы, стек, очередь и другие. Если мы правильно выберем структуру данных под решаемую задачу, мы сможем заметно упростить код и устранить запутанную логику.</p>
6
<p>Кроме массивов существует множество других структур данных: списки, хеш-таблицы, деревья, графы, стек, очередь и другие. Если мы правильно выберем структуру данных под решаемую задачу, мы сможем заметно упростить код и устранить запутанную логику.</p>
7
<p>Некоторые из перечисленных структур данных мы рассмотрим в курсах и проектах, другие придется изучить самостоятельно по книгам. В любом случае алгоритмы и структуры данных - это фундамент, на которую строится все остальное в разработке.</p>
7
<p>Некоторые из перечисленных структур данных мы рассмотрим в курсах и проектах, другие придется изучить самостоятельно по книгам. В любом случае алгоритмы и структуры данных - это фундамент, на которую строится все остальное в разработке.</p>
8
<p>Стоит разделять три понятия:</p>
8
<p>Стоит разделять три понятия:</p>
9
<ul><li><strong>Структура данных</strong>. Выше мы изучили этот термин</li>
9
<ul><li><strong>Структура данных</strong>. Выше мы изучили этот термин</li>
10
<li><strong>Конкретный тип данных</strong>. Типом данных может быть что угодно в зависимости от предпочтений создателей языка. Само это понятие всегда привязано к конкретному языку. Если бы создатели PHP захотели обозначать числа через тип данных Array, то никто не запретил бы эту абсурдную идею.</li>
10
<li><strong>Конкретный тип данных</strong>. Типом данных может быть что угодно в зависимости от предпочтений создателей языка. Само это понятие всегда привязано к конкретному языку. Если бы создатели PHP захотели обозначать числа через тип данных Array, то никто не запретил бы эту абсурдную идею.</li>
11
<li><strong>Абстрактный тип данных</strong>(АТД). Это теоретическое понятие, которое существует только на бумаге и в головах. Абстрактный тип данных определяется только операциями, которые можно выполнять над ним. При этом о способе хранения данных нам ничего не известно. АТД есть во всех языках, но везде его реализуют разные конкретные типы.</li>
11
<li><strong>Абстрактный тип данных</strong>(АТД). Это теоретическое понятие, которое существует только на бумаге и в головах. Абстрактный тип данных определяется только операциями, которые можно выполнять над ним. При этом о способе хранения данных нам ничего не известно. АТД есть во всех языках, но везде его реализуют разные конкретные типы.</li>
12
</ul><p>АТД нередко путают с понятием "структура данных". Более того, часто они имеют одно и то же название.</p>
12
</ul><p>АТД нередко путают с понятием "структура данных". Более того, часто они имеют одно и то же название.</p>
13
<p>В этом уроке мы разберем один из самых простых и важных абстрактных типов данных - стек.</p>
13
<p>В этом уроке мы разберем один из самых простых и важных абстрактных типов данных - стек.</p>
14
<h2>Стек</h2>
14
<h2>Стек</h2>
15
<p>Термином<strong>стек</strong>называют упорядоченную коллекцию элементов. В стеке добавление и удаление элементов всегда происходит с<strong>вершины стека</strong>- начала или конца коллекции:</p>
15
<p>Термином<strong>стек</strong>называют упорядоченную коллекцию элементов. В стеке добавление и удаление элементов всегда происходит с<strong>вершины стека</strong>- начала или конца коллекции:</p>
16
<p>У стека есть аналогии из реальной жизни. Само слово<em>stack</em>переводится с английского как "стопка", поэтому любую стопку можно рассматривать как стек. Например, чтобы изменить стопку книг, нужно добавить одну книгу сверху или убрать одну снизу.</p>
16
<p>У стека есть аналогии из реальной жизни. Само слово<em>stack</em>переводится с английского как "стопка", поэтому любую стопку можно рассматривать как стек. Например, чтобы изменить стопку книг, нужно добавить одну книгу сверху или убрать одну снизу.</p>
17
<p>Еще один пример стека - это стопка шипучих таблеток в упаковке:</p>
17
<p>Еще один пример стека - это стопка шипучих таблеток в упаковке:</p>
18
<p>Здесь есть дно, поэтому мы не можем сразу достать самую нижнюю таблетку. Открыв упаковку, мы заберем самую верхнюю таблетку, которую положили последней. Такие стеки называют<strong>LIFO</strong><em>(Last In First Out)</em>- последний элемент выходит из стека первым.</p>
18
<p>Здесь есть дно, поэтому мы не можем сразу достать самую нижнюю таблетку. Открыв упаковку, мы заберем самую верхнюю таблетку, которую положили последней. Такие стеки называют<strong>LIFO</strong><em>(Last In First Out)</em>- последний элемент выходит из стека первым.</p>
19
<p>Вспомните, как исполняется любая программа. Первые функции вызывают вторые, вторые вызывают третьи и так далее. Программа доходит до самой глубокой функции и получает ее значение. Чтобы вернуть это значение, программа запускает обратный процесс: переходит от самой глубокой функции к менее глубокой, поднимается на уровень выше, а потом еще выше вплоть до самой внешней функции. Другими словами, вызов функций - добавление элемента в стек, а возврат - снятие со стека.</p>
19
<p>Вспомните, как исполняется любая программа. Первые функции вызывают вторые, вторые вызывают третьи и так далее. Программа доходит до самой глубокой функции и получает ее значение. Чтобы вернуть это значение, программа запускает обратный процесс: переходит от самой глубокой функции к менее глубокой, поднимается на уровень выше, а потом еще выше вплоть до самой внешней функции. Другими словами, вызов функций - добавление элемента в стек, а возврат - снятие со стека.</p>
20
<p>Именно так все устроено на аппаратном уровне. Если во время выполнения произойдет ошибка, мы увидим<strong>трассировку стека</strong><em>(stack trace)</em>.</p>
20
<p>Именно так все устроено на аппаратном уровне. Если во время выполнения произойдет ошибка, мы увидим<strong>трассировку стека</strong><em>(stack trace)</em>.</p>
21
<p>Еще один пример из программирования - кнопка "Назад" в браузере. История посещений - это стек ссылок, в котором кнопка "Назад" извлекает последний элемент.</p>
21
<p>Еще один пример из программирования - кнопка "Назад" в браузере. История посещений - это стек ссылок, в котором кнопка "Назад" извлекает последний элемент.</p>
22
<p>Стек - абстрактный тип данных со следующим набором операций:</p>
22
<p>Стек - абстрактный тип данных со следующим набором операций:</p>
23
<ul><li>Добавить в стек (push)</li>
23
<ul><li>Добавить в стек (push)</li>
24
<li>Взять из стека (pop)</li>
24
<li>Взять из стека (pop)</li>
25
<li>Вернуть элемент с вершины стека без удаления (peek)</li>
25
<li>Вернуть элемент с вершины стека без удаления (peek)</li>
26
<li>Проверить на пустоту (isEmpty)</li>
26
<li>Проверить на пустоту (isEmpty)</li>
27
<li>Вернуть размер (size)</li>
27
<li>Вернуть размер (size)</li>
28
</ul><p>В PHP стек можно создать на основе массивов. Для этого используются функции array_push, array_pop, empty и count:</p>
28
</ul><p>В PHP стек можно создать на основе массивов. Для этого используются функции array_push, array_pop, empty и count:</p>
29
<p>Обратите внимание, что array_pop и array_push меняют исходный массив. При этом array_pop не только меняет массив, но и возвращает элемент, снятый со стека.</p>
29
<p>Обратите внимание, что array_pop и array_push меняют исходный массив. При этом array_pop не только меняет массив, но и возвращает элемент, снятый со стека.</p>
30
<p>Рассмотрим задачку, которую очень просто решить с помощью стека. Кстати, ее нередко задают на собеседованиях, чтобы проверить ваши знания о базовых структурах данных:</p>
30
<p>Рассмотрим задачку, которую очень просто решить с помощью стека. Кстати, ее нередко задают на собеседованиях, чтобы проверить ваши знания о базовых структурах данных:</p>
31
<blockquote><p><em>Реализуйте функцию, которая проверяет парные скобки <>, {}, () и []. Каждая открывающая скобка должна иметь закрывающую. Входом в функцию может быть ()<>{}.</em></p>
31
<blockquote><p><em>Реализуйте функцию, которая проверяет парные скобки <>, {}, () и []. Каждая открывающая скобка должна иметь закрывающую. Входом в функцию может быть ()<>{}.</em></p>
32
<p><em>Обратите внимание, что скобки не должны перекрываться. Например, сочетание [({)}] не пройдет проверку, потому что здесь есть перекрытие фигурных и круглых скобок.</em></p>
32
<p><em>Обратите внимание, что скобки не должны перекрываться. Например, сочетание [({)}] не пройдет проверку, потому что здесь есть перекрытие фигурных и круглых скобок.</em></p>
33
</blockquote><p>Решение со стеком выглядит так:</p>
33
</blockquote><p>Решение со стеком выглядит так:</p>
34
<ol><li>Проверяем первый элемент строки:<ul><li>Если это открывающая скобка, заносим ее в стек</li>
34
<ol><li>Проверяем первый элемент строки:<ul><li>Если это открывающая скобка, заносим ее в стек</li>
35
<li>Если это закрывающая скобка, достаем из стека последний добавленный элемент и смотрим, сочетаются ли эти скобки между собой. Неуспешная проверка покажет, что выражение не соответствует требуемому формату</li>
35
<li>Если это закрывающая скобка, достаем из стека последний добавленный элемент и смотрим, сочетаются ли эти скобки между собой. Неуспешная проверка покажет, что выражение не соответствует требуемому формату</li>
36
</ul></li>
36
</ul></li>
37
<li>Таким образом проходим все элементы:<ul><li>Если в конце строки мы видим пустой стек, то все хорошо</li>
37
<li>Таким образом проходим все элементы:<ul><li>Если в конце строки мы видим пустой стек, то все хорошо</li>
38
<li>Если в конце строки мы видим в стеке какие-то элементы, то проверка не прошла (такое может произойти, если были открывающие скобки в начале строки и не было закрывающих скобок в конце строки)</li>
38
<li>Если в конце строки мы видим в стеке какие-то элементы, то проверка не прошла (такое может произойти, если были открывающие скобки в начале строки и не было закрывающих скобок в конце строки)</li>
39
</ul></li>
39
</ul></li>
40
</ol><p>Перейдем к коду:</p>
40
</ol><p>Перейдем к коду:</p>
41
<p>Разберем его построчно:</p>
41
<p>Разберем его построчно:</p>
42
<p>Предположим, что на вход функции попала строка [{]. Функция проверки сработает так:</p>
42
<p>Предположим, что на вход функции попала строка [{]. Функция проверки сработает так:</p>
43
<ol><li>Первый символ [ входит в список открывающих скобок, заносим его в стек</li>
43
<ol><li>Первый символ [ входит в список открывающих скобок, заносим его в стек</li>
44
<li>Второй символ { тоже входит в список открывающих скобок, заносим его в стек</li>
44
<li>Второй символ { тоже входит в список открывающих скобок, заносим его в стек</li>
45
<li>Третий символ ] входит в список закрывающих скобок, поэтому достаем из стека последний символ {</li>
45
<li>Третий символ ] входит в список закрывающих скобок, поэтому достаем из стека последний символ {</li>
46
<li>Из символов { и ] составляем пару {], которая не проходит проверку</li>
46
<li>Из символов { и ] составляем пару {], которая не проходит проверку</li>
47
</ol><h2>Семантика</h2>
47
</ol><h2>Семантика</h2>
48
<p>Кажется, что эти функции будет удобно использовать в повседневной практике. Например, с их помощью можно извлекать из массива последний элемент. В целом, array_pop позволяет так сделать, но это не лучшее решение:</p>
48
<p>Кажется, что эти функции будет удобно использовать в повседневной практике. Например, с их помощью можно извлекать из массива последний элемент. В целом, array_pop позволяет так сделать, но это не лучшее решение:</p>
49
<ol><li>У этой операции есть побочный эффект - изменение исходного массива. Даже если массив нам больше не нужен, такой код вносит потенциальные проблемы, в будущем придется переписывать</li>
49
<ol><li>У этой операции есть побочный эффект - изменение исходного массива. Даже если массив нам больше не нужен, такой код вносит потенциальные проблемы, в будущем придется переписывать</li>
50
<li>Такой подход нарушает семантику. Инструменты нужно использовать по назначению - иначе рождается код, который декларирует одно, а делает совершенно другое. Увидев программу с array_pop или array_push, любой опытный программист сразу подозревает, что в ней массив используется как стек. Чтобы проанализировать такой код, нужно тратить дополнительные усилия</li>
50
<li>Такой подход нарушает семантику. Инструменты нужно использовать по назначению - иначе рождается код, который декларирует одно, а делает совершенно другое. Увидев программу с array_pop или array_push, любой опытный программист сразу подозревает, что в ней массив используется как стек. Чтобы проанализировать такой код, нужно тратить дополнительные усилия</li>
51
</ol>
51
</ol>