0 added
16 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Мы привыкли записывать математические выражения, опираясь на приоритет операций и на скобки. Именно поэтому</p>
1
<p>Мы привыкли записывать математические выражения, опираясь на приоритет операций и на скобки. Именно поэтому</p>
2
<p>+ 2 * 4 + 5 = 16, но (3 + 2)* (4 + 5) = 45.</p>
2
<p>+ 2 * 4 + 5 = 16, но (3 + 2)* (4 + 5) = 45.</p>
3
<p>Далеко не все помнят из школьной математики приоритеты сложения и умножения, поэтому в социальных сетях распространены задачи "Сколько будет 3 + 2 * 4 + 5?".</p>
3
<p>Далеко не все помнят из школьной математики приоритеты сложения и умножения, поэтому в социальных сетях распространены задачи "Сколько будет 3 + 2 * 4 + 5?".</p>
4
<p>Польский математик Ян Лукасевич около ста лет назад предложил необычный способ записи выражений, в котором не нужны ни приоритеты операций, ни скобки. Сейчас этот способ называют<strong>обратной польской записью</strong>.</p>
4
<p>Польский математик Ян Лукасевич около ста лет назад предложил необычный способ записи выражений, в котором не нужны ни приоритеты операций, ни скобки. Сейчас этот способ называют<strong>обратной польской записью</strong>.</p>
5
<p>Обратная запись непривычна и не получила широкого распространения, но ее можно встретить в таких языках программирования, как Forth и PostScript. * В обратной польской записи знак операции записывается не между операндами, а после них. Посмотрим, как это выглядит на примерах:</p>
5
<p>Обратная запись непривычна и не получила широкого распространения, но ее можно встретить в таких языках программирования, как Forth и PostScript. * В обратной польской записи знак операции записывается не между операндами, а после них. Посмотрим, как это выглядит на примерах:</p>
6
<ul><li>Обычная запись - 3 + 2, обратная - 3 2 +</li>
6
<ul><li>Обычная запись - 3 + 2, обратная - 3 2 +</li>
7
<li>Обычная запись - (3 + 2) * (4 + 5), обратная - 3 2 4 5 + + *</li>
7
<li>Обычная запись - (3 + 2) * (4 + 5), обратная - 3 2 4 5 + + *</li>
8
</ul><p>Операторы в обратной записи не всегда должны быть в конце. Например, 3 + 2 * 4 + 5 можно записать так:</p>
8
</ul><p>Операторы в обратной записи не всегда должны быть в конце. Например, 3 + 2 * 4 + 5 можно записать так:</p>
9
<p>3 2 + 4 5 + *</p>
9
<p>3 2 + 4 5 + *</p>
10
<p>Эта запись читается слева направо и воспринимается так:</p>
10
<p>Эта запись читается слева направо и воспринимается так:</p>
11
<ul><li>Число 3</li>
11
<ul><li>Число 3</li>
12
<li>Число 2</li>
12
<li>Число 2</li>
13
<li>Операция сложения</li>
13
<li>Операция сложения</li>
14
<li>Число 4</li>
14
<li>Число 4</li>
15
<li>Число 5</li>
15
<li>Число 5</li>
16
<li>Операция сложения</li>
16
<li>Операция сложения</li>
17
<li>Операция умножения</li>
17
<li>Операция умножения</li>
18
</ul><p>Преимущество обратных выражений в том, что они не вызывают разночтения.</p>
18
</ul><p>Преимущество обратных выражений в том, что они не вызывают разночтения.</p>
19
<p>Чтобы разобраться на примере, дальше мы пошагово вычислим это выражение:</p>
19
<p>Чтобы разобраться на примере, дальше мы пошагово вычислим это выражение:</p>
20
<p>3 2 + 4 5 + *</p>
20
<p>3 2 + 4 5 + *</p>
21
<p><strong>Шаг 1.</strong>Берем из стопки два верхних числа - 3 и 2. Выполняем над ними первую операцию - сложение:</p>
21
<p><strong>Шаг 1.</strong>Берем из стопки два верхних числа - 3 и 2. Выполняем над ними первую операцию - сложение:</p>
22
<p>3 + 2 = 5</p>
22
<p>3 + 2 = 5</p>
23
<p><strong>Шаг 2.</strong>Идем дальше по выражению - нужно взять следующие два значения и второй оператор. Берем эти значения и применяем к ним вторую операцию сложения:</p>
23
<p><strong>Шаг 2.</strong>Идем дальше по выражению - нужно взять следующие два значения и второй оператор. Берем эти значения и применяем к ним вторую операцию сложения:</p>
24
<p>4 + 5 = 9</p>
24
<p>4 + 5 = 9</p>
25
<p><strong>Шаг 3.</strong>Вспомним изначальное выражение:</p>
25
<p><strong>Шаг 3.</strong>Вспомним изначальное выражение:</p>
26
<p>3 2 4 5 + + *</p>
26
<p>3 2 4 5 + + *</p>
27
<p>Мы провели две операции сложения. Оставим только умножение и запишем в выражение результаты первых двух вычислений:</p>
27
<p>Мы провели две операции сложения. Оставим только умножение и запишем в выражение результаты первых двух вычислений:</p>
28
<p>5 9 *</p>
28
<p>5 9 *</p>
29
<p><strong>Шаг 4.</strong>Проводим последнюю операцию и получаем результат:</p>
29
<p><strong>Шаг 4.</strong>Проводим последнюю операцию и получаем результат:</p>
30
<p>5 * 9 = 45</p>
30
<p>5 * 9 = 45</p>
31
<p>Алгоритм вычисления очень прост, но требует новой для нас структуры данных. Представьте, что в вычислениях выше мы бы записывали каждое число на карточки и складывали бы из них стопку. Наверху стопки лежало бы число 45.</p>
31
<p>Алгоритм вычисления очень прост, но требует новой для нас структуры данных. Представьте, что в вычислениях выше мы бы записывали каждое число на карточки и складывали бы из них стопку. Наверху стопки лежало бы число 45.</p>
32
<p>В программировании такие стопки называются<strong>стеком</strong>- от английского stack, то есть стопка или кипа.</p>
32
<p>В программировании такие стопки называются<strong>стеком</strong>- от английского stack, то есть стопка или кипа.</p>
33
<p>В стеке, как и в стопке, мы имеем дело только с верхней карточкой -<strong>вершиной</strong>. Задача, которую решает стек - запомнить промежуточный результат для будущих вычислений.</p>
33
<p>В стеке, как и в стопке, мы имеем дело только с верхней карточкой -<strong>вершиной</strong>. Задача, которую решает стек - запомнить промежуточный результат для будущих вычислений.</p>
34
<p>В отличие от ранее изученных нами структур, стек обычно реализуют поверх других структур - массива или односвязного списка.</p>
34
<p>В отличие от ранее изученных нами структур, стек обычно реализуют поверх других структур - массива или односвязного списка.</p>
35
<h2>Реализация стека через массив</h2>
35
<h2>Реализация стека через массив</h2>
36
<p>Реализуя структуру данных, разработчик ради удобства может добавить в нее дополнительные методы.</p>
36
<p>Реализуя структуру данных, разработчик ради удобства может добавить в нее дополнительные методы.</p>
37
<p>Разные реализации могут быть непохожи друг на друга, но мы всегда ожидаем найти основные методы - конечно, у разных структур они разные. У стека должны быть реализованы три обязательных метода:</p>
37
<p>Разные реализации могут быть непохожи друг на друга, но мы всегда ожидаем найти основные методы - конечно, у разных структур они разные. У стека должны быть реализованы три обязательных метода:</p>
38
<ul><li>Метод push() помещает элемент на вершину стека, как карточку наверх стопки</li>
38
<ul><li>Метод push() помещает элемент на вершину стека, как карточку наверх стопки</li>
39
<li>Метод pop() убирает элемент с вершины и возвращает его</li>
39
<li>Метод pop() убирает элемент с вершины и возвращает его</li>
40
<li>Метод isEmpty() проверяет, пуст ли стек</li>
40
<li>Метод isEmpty() проверяет, пуст ли стек</li>
41
</ul><p>В JavaScript методы push() и pop() уже присутствуют в массиве, поэтому мы можем использовать их как есть:</p>
41
</ul><p>В JavaScript методы push() и pop() уже присутствуют в массиве, поэтому мы можем использовать их как есть:</p>
42
-
<p><strong>Javascript</strong></p>
43
-
<p><strong>Python</strong></p>
44
-
<p><strong>PHP</strong></p>
45
-
<p><strong>Java</strong></p>
46
<p>Метод isEmpty() возвращает true, потому что массив пуст - содержит 0 элементов.</p>
42
<p>Метод isEmpty() возвращает true, потому что массив пуст - содержит 0 элементов.</p>
47
<p>Воспользуемся нашим стеком, чтобы вычислить значение выражения 3 2 + 4 5 + *:</p>
43
<p>Воспользуемся нашим стеком, чтобы вычислить значение выражения 3 2 + 4 5 + *:</p>
48
-
<p><strong>Javascript</strong></p>
49
-
<p><strong>Python</strong></p>
50
-
<p><strong>PHP</strong></p>
51
-
<p><strong>Java</strong></p>
52
<p>Как видите, решение не очень сложное. Мы разбиваем строку с выражением на<strong>лексемы</strong>и обрабатываем каждую лексему в цикле.</p>
44
<p>Как видите, решение не очень сложное. Мы разбиваем строку с выражением на<strong>лексемы</strong>и обрабатываем каждую лексему в цикле.</p>
53
<p>Если лексема - это знак операции, то мы "снимаем" с вершины стека два числа, выполняем операцию и помещаем результат обратно на стек.</p>
45
<p>Если лексема - это знак операции, то мы "снимаем" с вершины стека два числа, выполняем операцию и помещаем результат обратно на стек.</p>
54
<p>При выполнении операций важно обращать внимание на порядок чисел. Числа в стеке расположены в порядке, обратном тому, в котором мы их туда помещали. Поэтому сначала мы извлекаем второй операнд b, а потом первый a.</p>
46
<p>При выполнении операций важно обращать внимание на порядок чисел. Числа в стеке расположены в порядке, обратном тому, в котором мы их туда помещали. Поэтому сначала мы извлекаем второй операнд b, а потом первый a.</p>
55
<p>Порядок операндов не важен для таких операций, как сложение и умножение, потому что 3 + 2 равно 2 + 3. Но при делении это уже не так - 3 / 2 не равно 2 / 3.</p>
47
<p>Порядок операндов не важен для таких операций, как сложение и умножение, потому что 3 + 2 равно 2 + 3. Но при делении это уже не так - 3 / 2 не равно 2 / 3.</p>
56
<p>Если лексема не похожа на знак операции, мы считаем, что это число. Тогда с помощью функции parseFloat() мы приводим строковую лексему к численному типу, чтобы ее можно было умножать и делить.</p>
48
<p>Если лексема не похожа на знак операции, мы считаем, что это число. Тогда с помощью функции parseFloat() мы приводим строковую лексему к численному типу, чтобы ее можно было умножать и делить.</p>
57
<p>После завершения цикла на вершине стека должен храниться результат.</p>
49
<p>После завершения цикла на вершине стека должен храниться результат.</p>
58
<h2>Реализация стека через односвязный список</h2>
50
<h2>Реализация стека через односвязный список</h2>
59
<p>Реализация стека с помощью массива выглядит простой, но вставка элементов в конец списка может приводить к задержкам. Чтобы избежать ресурсоемких операций, реализуем стек при помощи связного списка.</p>
51
<p>Реализация стека с помощью массива выглядит простой, но вставка элементов в конец списка может приводить к задержкам. Чтобы избежать ресурсоемких операций, реализуем стек при помощи связного списка.</p>
60
<p>Метод push() будет добавлять узел в начало списка, а pop() - удалять узел из начала:</p>
52
<p>Метод push() будет добавлять узел в начало списка, а pop() - удалять узел из начала:</p>
61
-
<p><strong>Javascript</strong></p>
62
-
<p><strong>Python</strong></p>
63
-
<p><strong>PHP</strong></p>
64
-
<p><strong>Java</strong></p>
65
<p>Метод isEmpty() проверяет, есть ли у списка голова. Если в списке нет ни одного узла, поле head будет содержать null - это означает, что стек пуст.</p>
53
<p>Метод isEmpty() проверяет, есть ли у списка голова. Если в списке нет ни одного узла, поле head будет содержать null - это означает, что стек пуст.</p>
66
<p>Методы в старом и новом классах Stack выглядят совершенно одинаково: имеют одинаковые названия, параметры и поведение. Поэтому вместо старой реализации нового стека мы можем использовать новую. Она будет немного быстрее, но чтобы это заметить, нам потребуются достаточно большие выражения.</p>
54
<p>Методы в старом и новом классах Stack выглядят совершенно одинаково: имеют одинаковые названия, параметры и поведение. Поэтому вместо старой реализации нового стека мы можем использовать новую. Она будет немного быстрее, но чтобы это заметить, нам потребуются достаточно большие выражения.</p>
67
<h2>Накопление и отправка изменения</h2>
55
<h2>Накопление и отправка изменения</h2>
68
<p>Сейчас мы практически никуда не выходим без смартфона. Кажется, что телефонные звонки - уже не самая нужная функция. В основном мы пользуемся мессенджерами, трекерами, календарями и программами учета калорий.</p>
56
<p>Сейчас мы практически никуда не выходим без смартфона. Кажется, что телефонные звонки - уже не самая нужная функция. В основном мы пользуемся мессенджерами, трекерами, календарями и программами учета калорий.</p>
69
<p>Программы часто хранят данные в облаке - то есть на серверах в интернете. Скажем, календарь нам нужен не только на смартфоне, но и на ноутбуке. А иногда нужно не только предоставить доступ с разных устройств, но и надежно хранить данные. В облаке надежнее, чем на смартфоне.</p>
57
<p>Программы часто хранят данные в облаке - то есть на серверах в интернете. Скажем, календарь нам нужен не только на смартфоне, но и на ноутбуке. А иногда нужно не только предоставить доступ с разных устройств, но и надежно хранить данные. В облаке надежнее, чем на смартфоне.</p>
70
<p>В теории, как только в программе появляются новые данные, она должна отправить их на сервер. Например, трекер понимает, что наша позиция изменилась и посылает в облако новые координаты.</p>
58
<p>В теории, как только в программе появляются новые данные, она должна отправить их на сервер. Например, трекер понимает, что наша позиция изменилась и посылает в облако новые координаты.</p>
71
<p>Но мобильная связь не всегда надежна и мы регулярно оказываемся без интернета. Простые программы в этом случае показывают ошибку подключения.</p>
59
<p>Но мобильная связь не всегда надежна и мы регулярно оказываемся без интернета. Простые программы в этом случае показывают ошибку подключения.</p>
72
<p>Однако, кратковременная потеря связи - это штатная ситуация и современные программы научились с ней справляться. Если связи нет, они складывают данные во временное хранилище, а когда связь появляется, отправляют их на сервер.</p>
60
<p>Однако, кратковременная потеря связи - это штатная ситуация и современные программы научились с ней справляться. Если связи нет, они складывают данные во временное хранилище, а когда связь появляется, отправляют их на сервер.</p>
73
<p>Но для хранения данных подойдет не любое хранилище. Представим, что программа-трекер складывает координаты в стек. Из стека координаты извлекаются в обратном порядке, как будто человек двигался задом наперед.</p>
61
<p>Но для хранения данных подойдет не любое хранилище. Представим, что программа-трекер складывает координаты в стек. Из стека координаты извлекаются в обратном порядке, как будто человек двигался задом наперед.</p>
74
<p>Нам нужна структура данных, похожая на стек и проводящая те же операции - поместить и извлечь. При этом в отличие от стека, она не должна менять порядок элементов. Это похоже на очередь в магазине, где люди обслуживаются в том же порядке, в котором они подошли к кассе.</p>
62
<p>Нам нужна структура данных, похожая на стек и проводящая те же операции - поместить и извлечь. При этом в отличие от стека, она не должна менять порядок элементов. Это похоже на очередь в магазине, где люди обслуживаются в том же порядке, в котором они подошли к кассе.</p>
75
<p>Такая структура действительно существует. Она называется<strong>очередь</strong>, а по английски - queue.</p>
63
<p>Такая структура действительно существует. Она называется<strong>очередь</strong>, а по английски - queue.</p>
76
<h2>Реализация очереди через двусвязный список</h2>
64
<h2>Реализация очереди через двусвязный список</h2>
77
<p>Как и в стеке, в очереди есть три основных метода:</p>
65
<p>Как и в стеке, в очереди есть три основных метода:</p>
78
<ul><li>push()</li>
66
<ul><li>push()</li>
79
<li>pop()</li>
67
<li>pop()</li>
80
<li>isEmpty()</li>
68
<li>isEmpty()</li>
81
</ul><p>Отличие в том, что в стеке элементы вставляются и извлекаются с одного конца. В очереди элементы вставляются с одного конца, а извлекаются из другого.</p>
69
</ul><p>Отличие в том, что в стеке элементы вставляются и извлекаются с одного конца. В очереди элементы вставляются с одного конца, а извлекаются из другого.</p>
82
<p>Как мы помним, односвязный список - не симметричная структура данных. Вставка и удаление элементов в начале списка выполняются гораздо быстрее, чем в конце, поэтому односвязный список не очень подходит для реализации очереди.</p>
70
<p>Как мы помним, односвязный список - не симметричная структура данных. Вставка и удаление элементов в начале списка выполняются гораздо быстрее, чем в конце, поэтому односвязный список не очень подходит для реализации очереди.</p>
83
<p>Зато для этой цели подходит двусвязный список, одинаково быстро работающий как в начале, так и в конце:</p>
71
<p>Зато для этой цели подходит двусвязный список, одинаково быстро работающий как в начале, так и в конце:</p>
84
-
<p><strong>Javascript</strong></p>
85
-
<p><strong>Python</strong></p>
86
-
<p><strong>PHP</strong></p>
87
-
<p><strong>Java</strong></p>
88
<p>Операция push() сводится к вставке узла в начало двусвязного списка. А реализация pop() останется вам в виде упражнения.</p>
72
<p>Операция push() сводится к вставке узла в начало двусвязного списка. А реализация pop() останется вам в виде упражнения.</p>
89
<h2>LIFO и FIFO</h2>
73
<h2>LIFO и FIFO</h2>
90
<p>Иногда в технической литературе вместо терминов<strong>стек</strong>и<strong>очередь</strong>, можно встретить другие термины:</p>
74
<p>Иногда в технической литературе вместо терминов<strong>стек</strong>и<strong>очередь</strong>, можно встретить другие термины:</p>
91
<ul><li><strong>Стек</strong>или<strong>список LIFO</strong>- Last In First Out (последним пришел - первым ушел)</li>
75
<ul><li><strong>Стек</strong>или<strong>список LIFO</strong>- Last In First Out (последним пришел - первым ушел)</li>
92
<li><strong>Очередь</strong>или<strong>список FIFO</strong>- First In First Out (первым пришел - первым ушел)</li>
76
<li><strong>Очередь</strong>или<strong>список FIFO</strong>- First In First Out (первым пришел - первым ушел)</li>
93
</ul><h2>Выводы</h2>
77
</ul><h2>Выводы</h2>
94
<p>Повторим ключевые моменты из этого урока:</p>
78
<p>Повторим ключевые моменты из этого урока:</p>
95
<ul><li>Стеки и очереди полезны для решения широкого круга задач</li>
79
<ul><li>Стеки и очереди полезны для решения широкого круга задач</li>
96
<li>Стек может быть реализован либо с помощью массива, либо с помощью односвязного списка</li>
80
<li>Стек может быть реализован либо с помощью массива, либо с помощью односвязного списка</li>
97
<li>Очередь может быть реализована с помощью двусвязного списка</li>
81
<li>Очередь может быть реализована с помощью двусвязного списка</li>
98
<li>Работу стека описывает принцип LIFO (последним пришел - первым ушел)</li>
82
<li>Работу стека описывает принцип LIFO (последним пришел - первым ушел)</li>
99
<li>Работу очереди описывает принцип FIFO (первым пришел - первым ушел)</li>
83
<li>Работу очереди описывает принцип FIFO (первым пришел - первым ушел)</li>
100
</ul>
84
</ul>