This commit is contained in:
krahets
2026-04-14 18:06:14 +08:00
parent 065a978848
commit e53a7f2498
93 changed files with 565 additions and 570 deletions
@@ -9,36 +9,36 @@ comments: true
- **Полный перебор**: реализуется через обход структуры данных, временная сложность равна $O(n)$ .
- **Адаптивный поиск**: использует особую организацию данных или априорную информацию, временная сложность может достигать $O(\log n)$ и даже $O(1)$ .
На практике **алгоритмы поиска с временной сложностью $O(\log n)$ обычно реализуются на основе стратегии "разделяй и властвуй"**, например двоичный поиск и деревья.
На практике **алгоритмы поиска с временной сложностью $O(\log n)$ обычно реализуются на основе стратегии «разделяй и властвуй»**, например двоичный поиск и деревья.
- На каждом шаге двоичный поиск раскладывает задачу (поиск целевого элемента в массиве) на более мелкую задачу (поиск целевого элемента в одной половине массива), и этот процесс продолжается, пока массив не станет пустым или пока не будет найден целевой элемент.
- Деревья являются типичными представителями идей "разделяй и властвуй"; в таких структурах данных, как двоичное дерево поиска, AVL-дерево и куча, временная сложность различных операций равна $O(\log n)$ .
- Деревья являются типичными представителями идей «разделяй и властвуй». В таких структурах данных, как двоичное дерево поиска, AVL-дерево и куча, временная сложность различных операций равна $O(\log n)$ .
Стратегия "разделяй и властвуй" для двоичного поиска выглядит следующим образом.
Стратегия «разделяй и властвуй» для двоичного поиска выглядит следующим образом.
- **Задача раскладывается на части**: двоичный поиск рекурсивно разбивает исходную задачу (поиск в массиве) на подзадачу (поиск в одной половине массива), и это достигается сравнением среднего элемента с целевым значением.
- **Подзадачи независимы**: в двоичном поиске на каждом шаге обрабатывается только одна подзадача, и она не зависит от других подзадач.
- **Решения подзадач не нужно объединять**: двоичный поиск нацелен на поиск конкретного элемента, поэтому объединять решения подзадач не требуется. Как только подзадача решена, одновременно считается решенной и исходная задача.
Иными словами, стратегия "разделяй и властвуй" повышает эффективность поиска потому, что при полном переборе за один шаг удается исключить только один вариант, **тогда как при поиске на основе "разделяй и властвуй" за один шаг можно исключить половину вариантов**.
Иными словами, стратегия «разделяй и властвуй» повышает эффективность поиска потому, что при полном переборе за один шаг удается исключить только один вариант, **тогда как при поиске на основе «разделяй и властвуй» за один шаг можно исключить половину вариантов**.
### 1.   Реализация двоичного поиска на основе "разделяй и властвуй"
### 1.   Реализация двоичного поиска на основе «разделяй и властвуй»
В предыдущих главах двоичный поиск реализовывался через итерацию. Теперь реализуем его с помощью стратегии "разделяй и властвуй", то есть через рекурсию.
В предыдущих главах двоичный поиск реализовывался через итерацию. Теперь реализуем его с помощью стратегии «разделяй и властвуй», то есть через рекурсию.
!!! question
Дан отсортированный массив `nums` длины $n$ , в котором все элементы уникальны. Найдите элемент `target` .
С точки зрения стратегии "разделяй и властвуй" обозначим подзадачу, соответствующую интервалу поиска $[i, j]$ , через $f(i, j)$ .
С точки зрения стратегии «разделяй и властвуй» обозначим подзадачу, соответствующую интервалу поиска $[i, j]$ , через $f(i, j)$ .
Начиная с исходной задачи $f(0, n-1)$ , выполняем двоичный поиск по следующим шагам.
1. Вычислить середину $m$ интервала поиска $[i, j]$ и с ее помощью исключить половину интервала.
2. Рекурсивно решить подзадачу вдвое меньшего размера; это может быть либо $f(i, m-1)$ , либо $f(m+1, j)$ .
2. Рекурсивно решить подзадачу вдвое меньшего размера. Это может быть либо $f(i, m-1)$ , либо $f(m+1, j)$ .
3. Повторять шаг `1.` и шаг `2.` , пока не будет найден `target` или пока интервал не станет пустым.
На рисунке 12-4 показан процесс применения стратегии "разделяй и властвуй" для поиска элемента $6$ в массиве.
На рисунке 12-4 показан процесс применения стратегии «разделяй и властвуй» для поиска элемента $6$ в массиве.
![Процесс двоичного поиска в стиле разделяй и властвуй](binary_search_recur.assets/binary_search_recur.png){ class="animation-figure" }
File diff suppressed because one or more lines are too long
@@ -4,12 +4,12 @@ comments: true
# 12.1   Стратегия разделяй и властвуй
<u>Разделяй и властвуй (divide and conquer)</u> - это очень важная и широко используемая стратегия построения алгоритмов. Обычно она реализуется через рекурсию и включает два этапа: "разделение" и "объединение".
<u>Разделяй и властвуй (divide and conquer)</u> - это очень важная и широко используемая стратегия построения алгоритмов. Обычно она реализуется через рекурсию и включает два этапа: «разделение» и «объединение».
1. **Разделение (этап декомпозиции)**: рекурсивно разбить исходную задачу на две или более подзадачи, пока не будет достигнута наименьшая подзадача.
2. **Объединение (этап синтеза)**: начиная с уже известных решений наименьших подзадач, снизу вверх объединять решения подзадач и тем самым получать решение исходной задачи.
Как показано на рисунке 12-1, "сортировка слиянием" является одним из типичных примеров применения стратегии "разделяй и властвуй".
Как показано на рисунке 12-1, «сортировка слиянием» является одним из типичных примеров применения стратегии «разделяй и властвуй».
1. **Разделение**: рекурсивно разделить исходный массив (исходную задачу) на два подмассива (подзадачи), пока в подмассиве не останется только один элемент (наименьшая подзадача).
2. **Объединение**: снизу вверх объединять упорядоченные подмассивы (решения подзадач), чтобы получить упорядоченный исходный массив (решение исходной задачи).
@@ -18,9 +18,9 @@ comments: true
<p align="center"> Рисунок 12-1 &nbsp; Стратегия разделяй и властвуй в сортировке слиянием </p>
## 12.1.1 &nbsp; Как определить задачу "разделяй и властвуй"
## 12.1.1 &nbsp; Как определить задачу «разделяй и властвуй»
Чтобы понять, подходит ли задача для решения методом "разделяй и властвуй", обычно можно ориентироваться на следующие критерии.
Чтобы понять, подходит ли задача для решения методом «разделяй и властвуй», обычно можно ориентироваться на следующие критерии.
1. **Задача раскладывается на части**: исходную задачу можно разбить на более мелкие и похожие подзадачи, причем такое разбиение можно применять рекурсивно.
2. **Подзадачи независимы**: подзадачи не пересекаются, не зависят друг от друга и могут решаться независимо.
@@ -32,15 +32,15 @@ comments: true
2. **Подзадачи независимы**: каждый подмассив можно сортировать отдельно (то есть каждую подзадачу можно решать независимо).
3. **Решения подзадач можно объединить**: два упорядоченных подмассива (решения подзадач) можно объединить в один упорядоченный массив (решение исходной задачи).
## 12.1.2 &nbsp; Повышение эффективности с помощью "разделяй и властвуй"
## 12.1.2 &nbsp; Повышение эффективности с помощью «разделяй и властвуй»
**Стратегия "разделяй и властвуй" не только позволяет эффективно решать алгоритмические задачи, но и часто повышает эффективность самих алгоритмов**. Именно поэтому быстрая сортировка, сортировка слиянием и пирамидальная сортировка обычно работают быстрее, чем сортировка выбором, пузырьком и вставками.
**Стратегия «разделяй и властвуй» не только позволяет эффективно решать алгоритмические задачи, но и часто повышает эффективность самих алгоритмов**. Именно поэтому быстрая сортировка, сортировка слиянием и пирамидальная сортировка обычно работают быстрее, чем сортировка выбором, пузырьком и вставками.
Тогда возникает естественный вопрос: **почему стратегия "разделяй и властвуй" повышает эффективность алгоритма и какова внутренняя логика этого подхода**? Иными словами, почему разбиение большой задачи на несколько подзадач, решение этих подзадач и последующее объединение их решений оказывается эффективнее, чем прямое решение исходной задачи? Этот вопрос можно рассмотреть с двух сторон: через число операций и через параллельные вычисления.
Тогда возникает естественный вопрос: **почему стратегия «разделяй и властвуй» повышает эффективность алгоритма и какова внутренняя логика этого подхода**? Иными словами, почему разбиение большой задачи на несколько подзадач, решение этих подзадач и последующее объединение их решений оказывается эффективнее, чем прямое решение исходной задачи? Этот вопрос можно рассмотреть с двух сторон: через число операций и через параллельные вычисления.
### 1. &nbsp; Оптимизация числа операций
Рассмотрим "сортировку пузырьком": для массива длины $n$ ей требуется $O(n^2)$ времени. Предположим, что мы разделим массив на два подмассива в середине, как показано на рисунке 12-2. Тогда само разбиение потребует $O(n)$ времени, сортировка каждого подмассива займет $O((n / 2)^2)$ времени, а объединение двух подмассивов потребует еще $O(n)$ времени. Общая временная сложность будет равна:
Рассмотрим «сортировку пузырьком»: для массива длины $n$ ей требуется $O(n^2)$ времени. Предположим, что мы разделим массив на два подмассива в середине, как показано на рисунке 12-2. Тогда само разбиение потребует $O(n)$ времени, сортировка каждого подмассива займет $O((n / 2)^2)$ времени, а объединение двух подмассивов потребует еще $O(n)$ времени. Общая временная сложность будет равна:
$$
O(n + (\frac{n}{2})^2 \times 2 + n) = O(\frac{n^2}{2} + 2n)
@@ -60,42 +60,42 @@ n(n - 4) & > 0
\end{aligned}
$$
**Это означает, что при $n > 4$ число операций после разбиения становится меньше, а значит, сортировка должна работать быстрее**. При этом важно заметить, что временная сложность после разбиения все еще остается квадратичной, то есть $O(n^2)$ ; уменьшается лишь константный множитель.
**Это означает, что при $n > 4$ число операций после разбиения становится меньше, а значит, сортировка должна работать быстрее**. При этом важно заметить, что временная сложность после разбиения все еще остается квадратичной, то есть $O(n^2)$. Уменьшается лишь константный множитель.
Если пойти дальше и **продолжать делить каждый подмассив пополам**, пока в нем не останется только один элемент, то мы фактически получим "сортировку слиянием", чья временная сложность равна $O(n \log n)$ .
Если пойти дальше и **продолжать делить каждый подмассив пополам**, пока в нем не останется только один элемент, то мы фактически получим «сортировку слиянием», чья временная сложность равна $O(n \log n)$ .
Можно пойти еще дальше и спросить: **что если задать несколько точек разделения** и равномерно разбить исходный массив на $k$ подмассивов? Такая ситуация очень похожа на блочную сортировку, которая особенно хорошо подходит для сортировки очень больших объемов данных и теоретически может достигать временной сложности $O(n + k)$ .
### 2. &nbsp; Оптимизация параллельных вычислений
Мы знаем, что подзадачи, порождаемые стратегией "разделяй и властвуй", являются независимыми, **а значит, их обычно можно решать параллельно**. Иначе говоря, "разделяй и властвуй" не только может уменьшить временную сложность алгоритма, **но и хорошо сочетается с параллельной оптимизацией на уровне системы**.
Мы знаем, что подзадачи, порождаемые стратегией «разделяй и властвуй», являются независимыми, **а значит, их обычно можно решать параллельно**. Иначе говоря, «разделяй и властвуй» не только может уменьшить временную сложность алгоритма, **но и хорошо сочетается с параллельной оптимизацией на уровне системы**.
Параллельная оптимизация особенно эффективна в среде с несколькими ядрами или несколькими процессорами, потому что система может одновременно обрабатывать разные подзадачи, лучше загружая вычислительные ресурсы и тем самым заметно сокращая общее время работы.
Например, в показанной ниже "блочной сортировке" большой объем данных равномерно распределяется по блокам. Тогда сортировку каждого блока можно поручить отдельным вычислительным единицам, а после завершения просто объединить результаты.
Например, в «блочной сортировке», показанной на рисунке 12-3, большой объем данных равномерно распределяется по блокам. Тогда сортировку каждого блока можно поручить отдельным вычислительным единицам, а после завершения просто объединить результаты.
![Параллельные вычисления в блочной сортировке](divide_and_conquer.assets/divide_and_conquer_parallel_computing.png){ class="animation-figure" }
<p align="center"> Рисунок 12-3 &nbsp; Параллельные вычисления в блочной сортировке </p>
## 12.1.3 &nbsp; Типичные применения стратегии "разделяй и властвуй"
## 12.1.3 &nbsp; Типичные применения стратегии «разделяй и властвуй»
С одной стороны, стратегию "разделяй и властвуй" можно использовать для решения многих классических алгоритмических задач.
С одной стороны, стратегию «разделяй и властвуй» можно использовать для решения многих классических алгоритмических задач.
- **Поиск ближайшей пары точек**: сначала множество точек делится на две части, затем ищется ближайшая пара в каждой части, а затем ближайшая пара, пересекающая границу между двумя частями.
- **Умножение больших чисел**: например, алгоритм Карацубы, который раскладывает умножение больших чисел на несколько умножений и сложений меньших чисел.
- **Умножение матриц**: например, алгоритм Штрассена, который раскладывает умножение больших матриц на несколько умножений и сложений матриц меньшего размера.
- **Задача о Ханойской башне**: задача о Ханойской башне решается рекурсивно и является типичным примером применения стратегии "разделяй и властвуй".
- **Подсчет инверсий**: если в последовательности предыдущее число больше следующего, то такая пара образует инверсию. Эту задачу можно решить с помощью идей "разделяй и властвуй", опираясь на сортировку слиянием.
- **Задача о Ханойской башне**: задача о Ханойской башне решается рекурсивно и является типичным примером применения стратегии «разделяй и властвуй».
- **Подсчет инверсий**: если в последовательности предыдущее число больше следующего, то такая пара образует инверсию. Эту задачу можно решить с помощью идей «разделяй и властвуй», опираясь на сортировку слиянием.
С другой стороны, стратегия "разделяй и властвуй" очень широко применяется при проектировании алгоритмов и структур данных.
С другой стороны, стратегия «разделяй и властвуй» очень широко применяется при проектировании алгоритмов и структур данных.
- **Двоичный поиск**: двоичный поиск делит отсортированный массив на две части по индексу середины, а затем, в зависимости от результата сравнения целевого значения со средним элементом, исключает одну из половин и повторяет ту же операцию на оставшемся интервале.
- **Сортировка слиянием**: она уже была рассмотрена в начале этого раздела, поэтому не будем повторяться.
- **Быстрая сортировка**: в ней выбирается опорное значение, после чего массив делится на два подмассива: один содержит элементы меньше опорного, а другой - больше. Затем такая же операция повторяется для обеих частей, пока в подмассиве не останется один элемент.
- **Блочная сортировка**: ее основная идея заключается в распределении данных по нескольким блокам, сортировке элементов внутри каждого блока и последующем последовательном извлечении элементов из блоков для построения отсортированного массива.
- **Деревья**: например, двоичные деревья поиска, AVL-деревья, красно-черные деревья, B-деревья, B+ деревья и т.д. Их операции поиска, вставки и удаления можно рассматривать как применение стратегии "разделяй и властвуй".
- **Кучи**: куча является особым видом полного двоичного дерева, а такие операции, как вставка, удаление и упорядочивание, по сути содержат идеи "разделяй и властвуй".
- **Хеш-таблицы**: хотя хеш-таблицы напрямую не используют стратегию "разделяй и властвуй", некоторые способы разрешения коллизий косвенно опираются на эту идею. Например, длинные цепочки в методе цепочек могут преобразовываться в красно-черные деревья для повышения эффективности поиска.
- **Деревья**: например, двоичные деревья поиска, AVL-деревья, красно-черные деревья, B-деревья, B+ деревья и т.д. Их операции поиска, вставки и удаления можно рассматривать как применение стратегии «разделяй и властвуй».
- **Кучи**: куча является особым видом полного двоичного дерева, а такие операции, как вставка, удаление и упорядочивание, по сути содержат идеи «разделяй и властвуй».
- **Хеш-таблицы**: хотя хеш-таблицы напрямую не используют стратегию «разделяй и властвуй», некоторые способы разрешения коллизий косвенно опираются на эту идею. Например, длинные цепочки в методе цепочек могут преобразовываться в красно-черные деревья для повышения эффективности поиска.
Нетрудно заметить, что **"разделяй и властвуй" - это "тихая" алгоритмическая идея**, скрыто присутствующая внутри самых разных алгоритмов и структур данных.
Нетрудно заметить, что **«разделяй и властвуй» - это «тихая» алгоритмическая идея**, скрыто присутствующая внутри самых разных алгоритмов и структур данных.
@@ -9,7 +9,7 @@ comments: true
!!! question
Даны три стержня, обозначенные как `A` , `B` и `C` . В начальном состоянии на стержне `A` находятся $n$ дисков, расположенных сверху вниз в порядке от меньшего к большему. Нужно переместить эти $n$ дисков на стержень `C` , сохранив их исходный порядок (как показано на рисунке 12-10). Во время перемещения дисков необходимо соблюдать следующие правила.
1. Диск можно снять только с вершины одного стержня и положить только на вершину другого стержня.
2. За один раз можно перемещать только один диск.
3. Меньший диск всегда должен лежать на большем.
@@ -58,7 +58,7 @@ comments: true
Для задачи $f(3)$ , то есть когда имеется три диска, ситуация становится сложнее.
Поскольку решения $f(1)$ и $f(2)$ уже известны, можно подойти к задаче с точки зрения стратегии "разделяй и властвуй" и **рассматривать два верхних диска на `A` как единое целое**, выполняя шаги, показанные на рисунке 12-13. Так три диска успешно перемещаются с `A` на `C` .
Поскольку решения $f(1)$ и $f(2)$ уже известны, можно подойти к задаче с точки зрения стратегии «разделяй и властвуй» и **рассматривать два верхних диска на `A` как единое целое**, выполняя шаги, показанные на рисунке 12-13. Так три диска успешно перемещаются с `A` на `C` .
1. Сделать `B` целевым стержнем, а `C` буферным, и переместить два диска с `A` на `B` .
2. Переместить оставшийся один диск с `A` напрямую на `C` .
@@ -80,7 +80,7 @@ comments: true
Иначе говоря, **мы разбиваем задачу $f(3)$ на две подзадачи $f(2)$ и одну подзадачу $f(1)$** . Если последовательно решить эти три подзадачи, исходная задача тоже будет решена. Это показывает, что подзадачи независимы и что их решения можно объединить.
Таким образом, можно сформулировать показанную на рисунке 12-14 стратегию "разделяй и властвуй" для задачи о Ханойской башне: исходная задача $f(n)$ разбивается на две подзадачи $f(n-1)$ и одну подзадачу $f(1)$ , которые затем решаются в следующем порядке.
Таким образом, можно сформулировать показанную на рисунке 12-14 стратегию «разделяй и властвуй» для задачи о Ханойской башне: исходная задача $f(n)$ разбивается на две подзадачи $f(n-1)$ и одну подзадачу $f(1)$ , которые затем решаются в следующем порядке.
1. Переместить $n-1$ дисков с `A` на `B` с помощью `C` .
2. Переместить оставшийся $1$ диск напрямую с `A` на `C` .
@@ -547,7 +547,7 @@ comments: true
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20move%28src%3A%20list%5Bint%5D%2C%20tar%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%BE%D0%B4%D0%B8%D0%BD%20%D0%B4%D0%B8%D1%81%D0%BA%22%22%22%0A%20%20%20%20%23%20%D0%A1%D0%BD%D1%8F%D1%82%D1%8C%20%D0%B4%D0%B8%D1%81%D0%BA%20%D1%81%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B%20src%0A%20%20%20%20pan%20%3D%20src.pop%28%29%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C%20%D0%B4%D0%B8%D1%81%D0%BA%20%D0%BD%D0%B0%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%83%20tar%0A%20%20%20%20tar.append%28pan%29%0A%0A%0Adef%20dfs%28i%3A%20int%2C%20src%3A%20list%5Bint%5D%2C%20buf%3A%20list%5Bint%5D%2C%20tar%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%D0%A0%D0%B5%D1%88%D0%B8%D1%82%D1%8C%20%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D1%83%20%D0%A5%D0%B0%D0%BD%D0%BE%D0%B9%D1%81%D0%BA%D0%BE%D0%B9%20%D0%B1%D0%B0%D1%88%D0%BD%D0%B8%20f%28i%29%22%22%22%0A%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%B2%20src%20%D0%BE%D1%81%D1%82%D0%B0%D0%BB%D1%81%D1%8F%20%D1%82%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE%20%D0%BE%D0%B4%D0%B8%D0%BD%20%D0%B4%D0%B8%D1%81%D0%BA%2C%20%D1%81%D1%80%D0%B0%D0%B7%D1%83%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%B5%D0%B3%D0%BE%20%D0%B2%20tar%0A%20%20%20%20if%20i%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20move%28src%2C%20tar%29%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%B4%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B0%20f%28i-1%29%3A%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%B8%D0%B5%20i-1%20%D0%B4%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%20%D0%B8%D0%B7%20src%20%D0%B2%20buf%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20tar%0A%20%20%20%20dfs%28i%20-%201%2C%20src%2C%20tar%2C%20buf%29%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%B4%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B0%20f%281%29%3A%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D1%88%D0%B8%D0%B9%D1%81%D1%8F%20%D0%BE%D0%B4%D0%B8%D0%BD%20%D0%B4%D0%B8%D1%81%D0%BA%20%D0%B8%D0%B7%20src%20%D0%B2%20tar%0A%20%20%20%20move%28src%2C%20tar%29%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%B4%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B0%20f%28i-1%29%3A%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%B8%D0%B5%20i-1%20%D0%B4%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%20%D0%B8%D0%B7%20buf%20%D0%B2%20tar%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20src%0A%20%20%20%20dfs%28i%20-%201%2C%20buf%2C%20src%2C%20tar%29%0A%0A%0Adef%20solve_hanota%28A%3A%20list%5Bint%5D%2C%20B%3A%20list%5Bint%5D%2C%20C%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%D0%A0%D0%B5%D1%88%D0%B8%D1%82%D1%8C%20%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D1%83%20%D0%A5%D0%B0%D0%BD%D0%BE%D0%B9%D1%81%D0%BA%D0%BE%D0%B9%20%D0%B1%D0%B0%D1%88%D0%BD%D0%B8%22%22%22%0A%20%20%20%20n%20%3D%20len%28A%29%0A%20%20%20%20%23%20%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%B8%D0%B5%20n%20%D0%B4%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%20%D0%B8%D0%B7%20A%20%D0%B2%20C%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20B%0A%20%20%20%20dfs%28n%2C%20A%2C%20B%2C%20C%29%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%D0%A5%D0%B2%D0%BE%D1%81%D1%82%20%D1%81%D0%BF%D0%B8%D1%81%D0%BA%D0%B0%20%D1%81%D0%BE%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D1%81%D1%82%D0%B2%D1%83%D0%B5%D1%82%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D0%B5%20%D1%81%D1%82%D0%BE%D0%BB%D0%B1%D1%86%D0%B0%0A%20%20%20%20A%20%3D%20%5B5%2C%204%2C%203%2C%202%2C%201%5D%0A%20%20%20%20B%20%3D%20%5B%5D%0A%20%20%20%20C%20%3D%20%5B%5D%0A%20%20%20%20print%28%22%D0%98%D1%81%D1%85%D0%BE%D0%B4%D0%BD%D0%BE%D0%B5%20%D1%81%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5%3A%22%29%0A%20%20%20%20print%28f%22A%20%3D%20%7BA%7D%22%29%0A%20%20%20%20print%28f%22B%20%3D%20%7BB%7D%22%29%0A%20%20%20%20print%28f%22C%20%3D%20%7BC%7D%22%29%0A%0A%20%20%20%20solve_hanota%28A%2C%20B%2C%20C%29%0A%0A%20%20%20%20print%28%22%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%20%D0%B7%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%89%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%B4%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%3A%22%29%0A%20%20%20%20print%28f%22A%20%3D%20%7BA%7D%22%29%0A%20%20%20%20print%28f%22B%20%3D%20%7BB%7D%22%29%0A%20%20%20%20print%28f%22C%20%3D%20%7BC%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=12&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20move%28src%3A%20list%5Bint%5D%2C%20tar%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%BE%D0%B4%D0%B8%D0%BD%20%D0%B4%D0%B8%D1%81%D0%BA%22%22%22%0A%20%20%20%20%23%20%D0%A1%D0%BD%D1%8F%D1%82%D1%8C%20%D0%B4%D0%B8%D1%81%D0%BA%20%D1%81%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B%20src%0A%20%20%20%20pan%20%3D%20src.pop%28%29%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C%20%D0%B4%D0%B8%D1%81%D0%BA%20%D0%BD%D0%B0%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%83%20tar%0A%20%20%20%20tar.append%28pan%29%0A%0A%0Adef%20dfs%28i%3A%20int%2C%20src%3A%20list%5Bint%5D%2C%20buf%3A%20list%5Bint%5D%2C%20tar%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%D0%A0%D0%B5%D1%88%D0%B8%D1%82%D1%8C%20%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D1%83%20%D0%A5%D0%B0%D0%BD%D0%BE%D0%B9%D1%81%D0%BA%D0%BE%D0%B9%20%D0%B1%D0%B0%D1%88%D0%BD%D0%B8%20f%28i%29%22%22%22%0A%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%B2%20src%20%D0%BE%D1%81%D1%82%D0%B0%D0%BB%D1%81%D1%8F%20%D1%82%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE%20%D0%BE%D0%B4%D0%B8%D0%BD%20%D0%B4%D0%B8%D1%81%D0%BA%2C%20%D1%81%D1%80%D0%B0%D0%B7%D1%83%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%B5%D0%B3%D0%BE%20%D0%B2%20tar%0A%20%20%20%20if%20i%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20move%28src%2C%20tar%29%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%B4%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B0%20f%28i-1%29%3A%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%B8%D0%B5%20i-1%20%D0%B4%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%20%D0%B8%D0%B7%20src%20%D0%B2%20buf%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20tar%0A%20%20%20%20dfs%28i%20-%201%2C%20src%2C%20tar%2C%20buf%29%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%B4%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B0%20f%281%29%3A%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D1%88%D0%B8%D0%B9%D1%81%D1%8F%20%D0%BE%D0%B4%D0%B8%D0%BD%20%D0%B4%D0%B8%D1%81%D0%BA%20%D0%B8%D0%B7%20src%20%D0%B2%20tar%0A%20%20%20%20move%28src%2C%20tar%29%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%B4%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B0%20f%28i-1%29%3A%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%B8%D0%B5%20i-1%20%D0%B4%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%20%D0%B8%D0%B7%20buf%20%D0%B2%20tar%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20src%0A%20%20%20%20dfs%28i%20-%201%2C%20buf%2C%20src%2C%20tar%29%0A%0A%0Adef%20solve_hanota%28A%3A%20list%5Bint%5D%2C%20B%3A%20list%5Bint%5D%2C%20C%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%D0%A0%D0%B5%D1%88%D0%B8%D1%82%D1%8C%20%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D1%83%20%D0%A5%D0%B0%D0%BD%D0%BE%D0%B9%D1%81%D0%BA%D0%BE%D0%B9%20%D0%B1%D0%B0%D1%88%D0%BD%D0%B8%22%22%22%0A%20%20%20%20n%20%3D%20len%28A%29%0A%20%20%20%20%23%20%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%B8%D0%B5%20n%20%D0%B4%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%20%D0%B8%D0%B7%20A%20%D0%B2%20C%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20B%0A%20%20%20%20dfs%28n%2C%20A%2C%20B%2C%20C%29%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%D0%A5%D0%B2%D0%BE%D1%81%D1%82%20%D1%81%D0%BF%D0%B8%D1%81%D0%BA%D0%B0%20%D1%81%D0%BE%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D1%81%D1%82%D0%B2%D1%83%D0%B5%D1%82%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D0%B5%20%D1%81%D1%82%D0%BE%D0%BB%D0%B1%D1%86%D0%B0%0A%20%20%20%20A%20%3D%20%5B5%2C%204%2C%203%2C%202%2C%201%5D%0A%20%20%20%20B%20%3D%20%5B%5D%0A%20%20%20%20C%20%3D%20%5B%5D%0A%20%20%20%20print%28%22%D0%98%D1%81%D1%85%D0%BE%D0%B4%D0%BD%D0%BE%D0%B5%20%D1%81%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5%3A%22%29%0A%20%20%20%20print%28f%22A%20%3D%20%7BA%7D%22%29%0A%20%20%20%20print%28f%22B%20%3D%20%7BB%7D%22%29%0A%20%20%20%20print%28f%22C%20%3D%20%7BC%7D%22%29%0A%0A%20%20%20%20solve_hanota%28A%2C%20B%2C%20C%29%0A%0A%20%20%20%20print%28%22%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%20%D0%B7%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%89%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%B4%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%3A%22%29%0A%20%20%20%20print%28f%22A%20%3D%20%7BA%7D%22%29%0A%20%20%20%20print%28f%22B%20%3D%20%7BB%7D%22%29%0A%20%20%20%20print%28f%22C%20%3D%20%7BC%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=12&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>
Как показано на рисунке 12-15, задача о Ханойской башне формирует дерево рекурсии высоты $n$ , в котором каждый узел представляет подзадачу и соответствует одному открытому вызову `dfs()` ; **поэтому временная сложность равна $O(2^n)$ , а пространственная сложность равна $O(n)$** .
Как показано на рисунке 12-15, задача о Ханойской башне формирует дерево рекурсии высоты $n$ , в котором каждый узел представляет подзадачу и соответствует одному открытому вызову `dfs()`. **Поэтому временная сложность равна $O(2^n)$ , а пространственная сложность равна $O(n)$** .
![Дерево рекурсии задачи о Ханойской башне](hanota_problem.assets/hanota_recursive_tree.png){ class="animation-figure" }
+2 -2
View File
@@ -10,8 +10,8 @@ icon: material/set-split
!!! abstract
Сложная задача раскладывается слой за слоем, и каждое новое разбиение делает ее проще.
Принцип "разделяй и властвуй" показывает важный факт: если начать с простого, многое перестает быть сложным.
Принцип «разделяй и властвуй» показывает важный факт: если начать с простого, многое перестает быть сложным.
## Содержание главы
@@ -6,12 +6,12 @@ comments: true
### 1. &nbsp; Ключевые выводы
- "Разделяй и властвуй" - это распространенная стратегия проектирования алгоритмов, которая включает два этапа: разделение (декомпозицию) и объединение (синтез), и обычно реализуется с помощью рекурсии.
- «Разделяй и властвуй» - это распространенная стратегия проектирования алгоритмов, которая включает два этапа: разделение (декомпозицию) и объединение (синтез), и обычно реализуется с помощью рекурсии.
- Критерии применимости этой стратегии к задаче включают: возможность разложения задачи, независимость подзадач и возможность объединения их решений.
- Сортировка слиянием является типичным применением стратегии "разделяй и властвуй": она рекурсивно делит массив на два равных по длине подмассива, пока не останется массив из одного элемента, после чего начинает поэтапное объединение.
- Использование стратегии "разделяй и властвуй" часто позволяет повысить эффективность алгоритма. С одной стороны, она уменьшает число операций; с другой - после разбиения способствует параллельной оптимизации на уровне системы.
- "Разделяй и властвуй" не только помогает решать многие алгоритмические задачи, но и широко используется при проектировании структур данных и алгоритмов, поэтому его можно встретить буквально повсюду.
- По сравнению с полным перебором адаптивный поиск работает эффективнее. Алгоритмы поиска со сложностью $O(\log n)$ обычно реализуются на основе стратегии "разделяй и властвуй".
- Двоичный поиск - еще одно типичное применение стратегии "разделяй и властвуй", в котором отсутствует шаг объединения решений подзадач. Его можно реализовать рекурсивно, опираясь на эту стратегию.
- Сортировка слиянием является типичным применением стратегии «разделяй и властвуй»: она рекурсивно делит массив на два равных по длине подмассива, пока не останется массив из одного элемента, после чего начинает поэтапное объединение.
- Использование стратегии «разделяй и властвуй» часто позволяет повысить эффективность алгоритма. С одной стороны, она уменьшает число операций. С другой - после разбиения способствует параллельной оптимизации на уровне системы.
- «Разделяй и властвуй» не только помогает решать многие алгоритмические задачи, но и широко используется при проектировании структур данных и алгоритмов, поэтому его можно встретить буквально повсюду.
- По сравнению с полным перебором адаптивный поиск работает эффективнее. Алгоритмы поиска со сложностью $O(\log n)$ обычно реализуются на основе стратегии «разделяй и властвуй».
- Двоичный поиск - еще одно типичное применение стратегии «разделяй и властвуй», в котором отсутствует шаг объединения решений подзадач. Его можно реализовать рекурсивно, опираясь на эту стратегию.
- В задаче построения двоичного дерева исходная задача построения дерева может быть разбита на две подзадачи: построение левого и правого поддеревьев, а реализуется это через разбиение индексных интервалов прямого и симметричного обходов.
- В задаче о Ханойской башне задача размера $n$ разбивается на две подзадачи размера $n-1$ и одну подзадачу размера $1$ . После последовательного решения этих трех подзадач исходная задача также оказывается решенной.