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 -9
View File
@@ -19,7 +19,7 @@ comments: true
### 1.   Поиск узла
Для заданного целевого значения узла `num` можно выполнить поиск, опираясь на свойства двоичного дерева поиска. Как показано на рисунках ниже, мы объявляем узел `cur` , стартуем от корня дерева `root` и циклически сравниваем значения `cur.val` и `num` .
Для заданного целевого значения узла `num` можно выполнить поиск, опираясь на свойства двоичного дерева поиска. Как показано на рисунке 7-17, мы объявляем узел `cur` , стартуем от корня дерева `root` и циклически сравниваем значения `cur.val` и `num` .
- Если `cur.val < num` , это означает, что целевой узел находится в правом поддереве `cur` , поэтому выполняем `cur = cur.right` .
- Если `cur.val > num` , это означает, что целевой узел находится в левом поддереве `cur` , поэтому выполняем `cur = cur.left` .
@@ -345,7 +345,7 @@ comments: true
### 2. &nbsp; Вставка узла
Пусть дан элемент `num` , который нужно вставить. Чтобы сохранить свойство двоичного дерева поиска "левое поддерево < корень < правое поддерево", процесс вставки выглядит следующим образом.
Пусть дан элемент `num` , который нужно вставить. Чтобы сохранить свойство двоичного дерева поиска «левое поддерево < корень < правое поддерево», процесс вставки показан на рисунке 7-18.
1. **Найти позицию для вставки**: как и в операции поиска, начиная от корня, мы циклически спускаемся вниз в зависимости от соотношения между текущим значением узла и `num` , пока не выйдем за листовой узел (то есть не дойдем до `None` ).
2. **Вставить узел в найденную позицию**: инициализировать узел `num` и поставить его на место этого `None` .
@@ -811,7 +811,7 @@ comments: true
### 3. &nbsp; Удаление узла
Сначала нужно найти в двоичном дереве целевой узел, а затем удалить его. Как и при вставке, после удаления необходимо сохранить свойство двоичного дерева поиска: "левое поддерево < корень < правое поддерево". Поэтому в зависимости от числа дочерних узлов у удаляемого узла, то есть для случаев со степенью 0, 1 и 2, выполняются разные операции удаления.
Сначала нужно найти в двоичном дереве целевой узел, а затем удалить его. Как и при вставке, после удаления необходимо сохранить свойство двоичного дерева поиска: «левое поддерево < корень < правое поддерево». Поэтому в зависимости от числа дочерних узлов у удаляемого узла, то есть для случаев со степенью 0, 1 и 2, выполняются разные операции удаления.
Как показано на рисунке 7-19, когда степень удаляемого узла равна $0$ , это значит, что узел является листом и может быть удален напрямую.
@@ -825,11 +825,11 @@ comments: true
<p align="center"> Рисунок 7-20 &nbsp; Удаление узла в двоичном дереве поиска (степень 1) </p>
Когда степень удаляемого узла равна $2$ , мы уже не можем удалить его напрямую и должны использовать для замены другой узел. Чтобы сохранить свойство двоичного дерева поиска "левое поддерево $<$ корень $<$ правое поддерево", **этим узлом может быть минимальный узел правого поддерева или максимальный узел левого поддерева**.
Когда степень удаляемого узла равна $2$ , мы уже не можем удалить его напрямую и должны использовать для замены другой узел. Чтобы сохранить свойство двоичного дерева поиска «левое поддерево $<$ корень $<$ правое поддерево», **этим узлом может быть минимальный узел правого поддерева или максимальный узел левого поддерева**.
Предположим, мы выбираем минимальный узел правого поддерева, то есть следующий узел в симметричном обходе. Тогда процесс удаления выглядит так.
Предположим, мы выбираем минимальный узел правого поддерева, то есть следующий узел в симметричном обходе. Тогда процесс удаления показан на рисунке 7-21.
1. Найти следующий узел в "последовательности симметричного обхода" для удаляемого узла и обозначить его как `tmp` .
1. Найти следующий узел в «последовательности симметричного обхода» для удаляемого узла и обозначить его как `tmp` .
2. Значением `tmp` перезаписать значение удаляемого узла, а затем рекурсивно удалить узел `tmp` из дерева.
=== "<1>"
@@ -1595,7 +1595,7 @@ comments: true
### 4. &nbsp; Упорядоченность симметричного обхода
Как показано на рисунке 7-22, симметричный обход двоичного дерева следует порядку "лево $\rightarrow$ корень $\rightarrow$ право", а двоичное дерево поиска удовлетворяет соотношению "левый дочерний узел $<$ корень $<$ правый дочерний узел".
Как показано на рисунке 7-22, симметричный обход двоичного дерева следует порядку «лево $\rightarrow$ корень $\rightarrow$ право», а двоичное дерево поиска удовлетворяет соотношению «левый дочерний узел $<$ корень $<$ правый дочерний узел».
Это означает, что при симметричном обходе двоичного дерева поиска мы всегда сначала будем посещать следующий минимальный узел, и отсюда получается важное свойство: **последовательность симметричного обхода двоичного дерева поиска является возрастающей**.
@@ -1607,7 +1607,7 @@ comments: true
## 7.4.2 &nbsp; Эффективность двоичного дерева поиска
Для заданного набора данных можно рассмотреть хранение либо в массиве, либо в двоичном дереве поиска. Из таблицы ниже видно, что временная сложность операций двоичного дерева поиска имеет логарифмический порядок и обеспечивает стабильную высокую производительность. Только в сценариях с очень частыми вставками и редкими поисками и удалениями массив может быть эффективнее, чем двоичное дерево поиска.
Для заданного набора данных можно рассмотреть хранение либо в массиве, либо в двоичном дереве поиска. Как видно по данным в таблице 7-2, временная сложность операций двоичного дерева поиска имеет логарифмический порядок и обеспечивает стабильную высокую производительность. Только в сценариях с очень частыми вставками и редкими поисками и удалениями массив может быть эффективнее, чем двоичное дерево поиска.
<p align="center"> Таблица 7-2 &nbsp; Сравнение эффективности массива и дерева поиска </p>
@@ -1621,7 +1621,7 @@ comments: true
</div>
В идеальном случае двоичное дерево поиска является "сбалансированным", и тогда любой узел можно найти за $\log n$ итераций.
В идеальном случае двоичное дерево поиска является «сбалансированным», и тогда любой узел можно найти за $\log n$ итераций.
Однако если в двоичное дерево поиска непрерывно вставлять и удалять узлы, оно может выродиться в связный список, как показано на рисунке 7-23. Тогда временная сложность различных операций тоже вырождается до $O(n)$ .