HTML Diff
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>