mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-07 19:46:29 +08:00
build
This commit is contained in:
1357
ru/docs/chapter_tree/array_representation_of_tree.md
Normal file
1357
ru/docs/chapter_tree/array_representation_of_tree.md
Normal file
File diff suppressed because it is too large
Load Diff
3368
ru/docs/chapter_tree/avl_tree.md
Normal file
3368
ru/docs/chapter_tree/avl_tree.md
Normal file
File diff suppressed because it is too large
Load Diff
1777
ru/docs/chapter_tree/binary_search_tree.md
Normal file
1777
ru/docs/chapter_tree/binary_search_tree.md
Normal file
File diff suppressed because it is too large
Load Diff
698
ru/docs/chapter_tree/binary_tree.md
Normal file
698
ru/docs/chapter_tree/binary_tree.md
Normal file
@@ -0,0 +1,698 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 7.1 Двоичное дерево
|
||||
|
||||
<u>Двоичное дерево (binary tree)</u> - это нелинейная структура данных, представляющая отношения порождения между "предками" и "потомками" и отражающая логику "разделения надвое". Подобно связному списку, базовой единицей двоичного дерева является узел; каждый узел содержит значение, ссылку на левого дочернего узла и ссылку на правого дочернего узла.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title=""
|
||||
class TreeNode:
|
||||
"""Класс узла двоичного дерева"""
|
||||
def __init__(self, val: int):
|
||||
self.val: int = val # Значение узла
|
||||
self.left: TreeNode | None = None # Ссылка на левого дочернего узла
|
||||
self.right: TreeNode | None = None # Ссылка на правого дочернего узла
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title=""
|
||||
/* Структура узла двоичного дерева */
|
||||
struct TreeNode {
|
||||
int val; // Значение узла
|
||||
TreeNode *left; // Указатель на левого дочернего узла
|
||||
TreeNode *right; // Указатель на правого дочернего узла
|
||||
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
|
||||
};
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title=""
|
||||
/* Класс узла двоичного дерева */
|
||||
class TreeNode {
|
||||
int val; // Значение узла
|
||||
TreeNode left; // Ссылка на левого дочернего узла
|
||||
TreeNode right; // Ссылка на правого дочернего узла
|
||||
TreeNode(int x) { val = x; }
|
||||
}
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title=""
|
||||
/* Класс узла двоичного дерева */
|
||||
class TreeNode(int? x) {
|
||||
public int? val = x; // Значение узла
|
||||
public TreeNode? left; // Ссылка на левого дочернего узла
|
||||
public TreeNode? right; // Ссылка на правого дочернего узла
|
||||
}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title=""
|
||||
/* Структура узла двоичного дерева */
|
||||
type TreeNode struct {
|
||||
Val int
|
||||
Left *TreeNode
|
||||
Right *TreeNode
|
||||
}
|
||||
/* Конструктор */
|
||||
func NewTreeNode(v int) *TreeNode {
|
||||
return &TreeNode{
|
||||
Left: nil, // Указатель на левого дочернего узла
|
||||
Right: nil, // Указатель на правого дочернего узла
|
||||
Val: v, // Значение узла
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title=""
|
||||
/* Класс узла двоичного дерева */
|
||||
class TreeNode {
|
||||
var val: Int // Значение узла
|
||||
var left: TreeNode? // Ссылка на левого дочернего узла
|
||||
var right: TreeNode? // Ссылка на правого дочернего узла
|
||||
|
||||
init(x: Int) {
|
||||
val = x
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title=""
|
||||
/* Класс узла двоичного дерева */
|
||||
class TreeNode {
|
||||
val; // Значение узла
|
||||
left; // Указатель на левого дочернего узла
|
||||
right; // Указатель на правого дочернего узла
|
||||
constructor(val, left, right) {
|
||||
this.val = val === undefined ? 0 : val;
|
||||
this.left = left === undefined ? null : left;
|
||||
this.right = right === undefined ? null : right;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title=""
|
||||
/* Класс узла двоичного дерева */
|
||||
class TreeNode {
|
||||
val: number;
|
||||
left: TreeNode | null;
|
||||
right: TreeNode | null;
|
||||
|
||||
constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
|
||||
this.val = val === undefined ? 0 : val; // Значение узла
|
||||
this.left = left === undefined ? null : left; // Ссылка на левого дочернего узла
|
||||
this.right = right === undefined ? null : right; // Ссылка на правого дочернего узла
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
/* Класс узла двоичного дерева */
|
||||
class TreeNode {
|
||||
int val; // Значение узла
|
||||
TreeNode? left; // Ссылка на левого дочернего узла
|
||||
TreeNode? right; // Ссылка на правого дочернего узла
|
||||
TreeNode(this.val, [this.left, this.right]);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title=""
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
/* Структура узла двоичного дерева */
|
||||
struct TreeNode {
|
||||
val: i32, // Значение узла
|
||||
left: Option<Rc<RefCell<TreeNode>>>, // Ссылка на левого дочернего узла
|
||||
right: Option<Rc<RefCell<TreeNode>>>, // Ссылка на правого дочернего узла
|
||||
}
|
||||
|
||||
impl TreeNode {
|
||||
/* Конструктор */
|
||||
fn new(val: i32) -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self {
|
||||
val,
|
||||
left: None,
|
||||
right: None
|
||||
}))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title=""
|
||||
/* Структура узла двоичного дерева */
|
||||
typedef struct TreeNode {
|
||||
int val; // Значение узла
|
||||
int height; // Высота узла
|
||||
struct TreeNode *left; // Указатель на левого дочернего узла
|
||||
struct TreeNode *right; // Указатель на правого дочернего узла
|
||||
} TreeNode;
|
||||
|
||||
/* Конструктор */
|
||||
TreeNode *newTreeNode(int val) {
|
||||
TreeNode *node;
|
||||
|
||||
node = (TreeNode *)malloc(sizeof(TreeNode));
|
||||
node->val = val;
|
||||
node->height = 0;
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
return node;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title=""
|
||||
/* Класс узла двоичного дерева */
|
||||
class TreeNode(val _val: Int) { // Значение узла
|
||||
val left: TreeNode? = null // Ссылка на левого дочернего узла
|
||||
val right: TreeNode? = null // Ссылка на правого дочернего узла
|
||||
}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title=""
|
||||
### Класс узла двоичного дерева ###
|
||||
class TreeNode
|
||||
attr_accessor :val # Значение узла
|
||||
attr_accessor :left # Ссылка на левого дочернего узла
|
||||
attr_accessor :right # Ссылка на правого дочернего узла
|
||||
|
||||
def initialize(val)
|
||||
@val = val
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Каждый узел имеет две ссылки (указателя), которые соответственно указывают на <u>левого дочернего узла (left-child node)</u> и <u>правого дочернего узла (right-child node)</u>; данный узел называется <u>родительским узлом (parent node)</u> для этих двух дочерних узлов. Если задан некоторый узел двоичного дерева, то дерево, образованное его левым дочерним узлом и всеми узлами ниже него, называется <u>левым поддеревом (left subtree)</u> этого узла; аналогично определяется <u>правое поддерево (right subtree)</u>.
|
||||
|
||||
**В двоичном дереве, кроме листовых узлов, все остальные узлы содержат дочерние узлы и непустые поддеревья**. Как показано на рисунке 7-1, если рассматривать "узел 2" как родительский, то его левым и правым дочерними узлами будут "узел 4" и "узел 5"; левое поддерево - это "узел 4 и дерево ниже него", а правое поддерево - это "узел 5 и дерево ниже него".
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-1 Родительский узел, дочерние узлы и поддеревья </p>
|
||||
|
||||
## 7.1.1 Распространенные термины двоичного дерева
|
||||
|
||||
Распространенные термины двоичного дерева показаны на рисунке 7-2.
|
||||
|
||||
- <u>Корневой узел (root node)</u>: узел, расположенный на верхнем уровне двоичного дерева и не имеющий родительского узла.
|
||||
- <u>Листовой узел (leaf node)</u>: узел без дочерних узлов; оба его указателя направлены на `None` .
|
||||
- <u>Ребро (edge)</u>: отрезок, соединяющий два узла, то есть ссылка (указатель) между узлами.
|
||||
- <u>Уровень (level)</u> узла: увеличивается сверху вниз; уровень корневого узла равен 1 .
|
||||
- <u>Степень (degree)</u> узла: число дочерних узлов данного узла. В двоичном дереве возможны степени 0, 1, 2 .
|
||||
- <u>Высота (height)</u> двоичного дерева: число ребер от корневого узла до самого удаленного листового узла.
|
||||
- <u>Глубина (depth)</u> узла: число ребер от корневого узла до данного узла.
|
||||
- <u>Высота (height)</u> узла: число ребер от самого удаленного листового узла до данного узла.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-2 Распространенные термины двоичного дерева </p>
|
||||
|
||||
!!! tip
|
||||
|
||||
Обрати внимание: обычно под "высотой" и "глубиной" понимают "число пройденных ребер", но в некоторых задачах или учебниках их могут определять как "число пройденных узлов". В таком случае и высоту, и глубину нужно увеличить на 1 .
|
||||
|
||||
## 7.1.2 Базовые операции двоичного дерева
|
||||
|
||||
### 1. Инициализация двоичного дерева
|
||||
|
||||
Как и в связном списке, сначала инициализируются узлы, а затем между ними строятся ссылки (указатели).
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="binary_tree.py"
|
||||
# Инициализация двоичного дерева
|
||||
# Инициализация узлов
|
||||
n1 = TreeNode(val=1)
|
||||
n2 = TreeNode(val=2)
|
||||
n3 = TreeNode(val=3)
|
||||
n4 = TreeNode(val=4)
|
||||
n5 = TreeNode(val=5)
|
||||
# Построение ссылок (указателей) между узлами
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
n2.right = n5
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="binary_tree.cpp"
|
||||
/* Инициализация двоичного дерева */
|
||||
// Инициализация узлов
|
||||
TreeNode* n1 = new TreeNode(1);
|
||||
TreeNode* n2 = new TreeNode(2);
|
||||
TreeNode* n3 = new TreeNode(3);
|
||||
TreeNode* n4 = new TreeNode(4);
|
||||
TreeNode* n5 = new TreeNode(5);
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1->left = n2;
|
||||
n1->right = n3;
|
||||
n2->left = n4;
|
||||
n2->right = n5;
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="binary_tree.java"
|
||||
// Инициализация узлов
|
||||
TreeNode n1 = new TreeNode(1);
|
||||
TreeNode n2 = new TreeNode(2);
|
||||
TreeNode n3 = new TreeNode(3);
|
||||
TreeNode n4 = new TreeNode(4);
|
||||
TreeNode n5 = new TreeNode(5);
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
n2.right = n5;
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="binary_tree.cs"
|
||||
/* Инициализация двоичного дерева */
|
||||
// Инициализация узлов
|
||||
TreeNode n1 = new(1);
|
||||
TreeNode n2 = new(2);
|
||||
TreeNode n3 = new(3);
|
||||
TreeNode n4 = new(4);
|
||||
TreeNode n5 = new(5);
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
n2.right = n5;
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="binary_tree.go"
|
||||
/* Инициализация двоичного дерева */
|
||||
// Инициализация узлов
|
||||
n1 := NewTreeNode(1)
|
||||
n2 := NewTreeNode(2)
|
||||
n3 := NewTreeNode(3)
|
||||
n4 := NewTreeNode(4)
|
||||
n5 := NewTreeNode(5)
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1.Left = n2
|
||||
n1.Right = n3
|
||||
n2.Left = n4
|
||||
n2.Right = n5
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="binary_tree.swift"
|
||||
// Инициализация узлов
|
||||
let n1 = TreeNode(x: 1)
|
||||
let n2 = TreeNode(x: 2)
|
||||
let n3 = TreeNode(x: 3)
|
||||
let n4 = TreeNode(x: 4)
|
||||
let n5 = TreeNode(x: 5)
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
n2.right = n5
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="binary_tree.js"
|
||||
/* Инициализация двоичного дерева */
|
||||
// Инициализация узлов
|
||||
let n1 = new TreeNode(1),
|
||||
n2 = new TreeNode(2),
|
||||
n3 = new TreeNode(3),
|
||||
n4 = new TreeNode(4),
|
||||
n5 = new TreeNode(5);
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
n2.right = n5;
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="binary_tree.ts"
|
||||
/* Инициализация двоичного дерева */
|
||||
// Инициализация узлов
|
||||
let n1 = new TreeNode(1),
|
||||
n2 = new TreeNode(2),
|
||||
n3 = new TreeNode(3),
|
||||
n4 = new TreeNode(4),
|
||||
n5 = new TreeNode(5);
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
n2.right = n5;
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="binary_tree.dart"
|
||||
/* Инициализация двоичного дерева */
|
||||
// Инициализация узлов
|
||||
TreeNode n1 = new TreeNode(1);
|
||||
TreeNode n2 = new TreeNode(2);
|
||||
TreeNode n3 = new TreeNode(3);
|
||||
TreeNode n4 = new TreeNode(4);
|
||||
TreeNode n5 = new TreeNode(5);
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
n2.right = n5;
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="binary_tree.rs"
|
||||
// Инициализация узлов
|
||||
let n1 = TreeNode::new(1);
|
||||
let n2 = TreeNode::new(2);
|
||||
let n3 = TreeNode::new(3);
|
||||
let n4 = TreeNode::new(4);
|
||||
let n5 = TreeNode::new(5);
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1.borrow_mut().left = Some(n2.clone());
|
||||
n1.borrow_mut().right = Some(n3);
|
||||
n2.borrow_mut().left = Some(n4);
|
||||
n2.borrow_mut().right = Some(n5);
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="binary_tree.c"
|
||||
/* Инициализация двоичного дерева */
|
||||
// Инициализация узлов
|
||||
TreeNode *n1 = newTreeNode(1);
|
||||
TreeNode *n2 = newTreeNode(2);
|
||||
TreeNode *n3 = newTreeNode(3);
|
||||
TreeNode *n4 = newTreeNode(4);
|
||||
TreeNode *n5 = newTreeNode(5);
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1->left = n2;
|
||||
n1->right = n3;
|
||||
n2->left = n4;
|
||||
n2->right = n5;
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="binary_tree.kt"
|
||||
// Инициализация узлов
|
||||
val n1 = TreeNode(1)
|
||||
val n2 = TreeNode(2)
|
||||
val n3 = TreeNode(3)
|
||||
val n4 = TreeNode(4)
|
||||
val n5 = TreeNode(5)
|
||||
// Построение ссылок (указателей) между узлами
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
n2.right = n5
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="binary_tree.rb"
|
||||
# Инициализация двоичного дерева
|
||||
# Инициализация узлов
|
||||
n1 = TreeNode.new(1)
|
||||
n2 = TreeNode.new(2)
|
||||
n3 = TreeNode.new(3)
|
||||
n4 = TreeNode.new(4)
|
||||
n5 = TreeNode.new(5)
|
||||
# Построение ссылок (указателей) между узлами
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
n2.right = n5
|
||||
```
|
||||
|
||||
??? pythontutor "Визуализация выполнения"
|
||||
|
||||
https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22%D0%9A%D0%BB%D0%B0%D1%81%D1%81%20%D1%83%D0%B7%D0%BB%D0%B0%20%D0%B4%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%BE%D0%B3%D0%BE%20%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B0%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%D0%97%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%83%D0%B7%D0%BB%D0%B0%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D0%B0%20%D0%BD%D0%B0%20%D0%BB%D0%B5%D0%B2%D1%8B%D0%B9%20%D0%B4%D0%BE%D1%87%D0%B5%D1%80%D0%BD%D0%B8%D0%B9%20%D1%83%D0%B7%D0%B5%D0%BB%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D0%B0%20%D0%BD%D0%B0%20%D0%BF%D1%80%D0%B0%D0%B2%D1%8B%D0%B9%20%D0%B4%D0%BE%D1%87%D0%B5%D1%80%D0%BD%D0%B8%D0%B9%20%D1%83%D0%B7%D0%B5%D0%BB%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%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D0%B4%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%BE%D0%B5%20%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D1%83%D0%B7%D0%B5%D0%BB%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B8%D1%82%D1%8C%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%20%D0%BC%D0%B5%D0%B6%D0%B4%D1%83%20%D1%83%D0%B7%D0%BB%D0%B0%D0%BC%D0%B8%20%28%D1%83%D0%BA%D0%B0%D0%B7%D0%B0%D1%82%D0%B5%D0%BB%D0%B8%29%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
|
||||
|
||||
### 2. Вставка и удаление узлов
|
||||
|
||||
Как и в связном списке, вставка и удаление узлов в двоичном дереве могут выполняться через изменение указателей. На рисунке 7-3 приведен пример.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-3 Вставка и удаление узлов в двоичном дереве </p>
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="binary_tree.py"
|
||||
# Вставка и удаление узлов
|
||||
p = TreeNode(0)
|
||||
# Вставить узел P между n1 -> n2
|
||||
n1.left = p
|
||||
p.left = n2
|
||||
# Удалить узел P
|
||||
n1.left = n2
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="binary_tree.cpp"
|
||||
/* Вставка и удаление узлов */
|
||||
TreeNode* P = new TreeNode(0);
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1->left = P;
|
||||
P->left = n2;
|
||||
// Удалить узел P
|
||||
n1->left = n2;
|
||||
// Освободить память
|
||||
delete P;
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="binary_tree.java"
|
||||
TreeNode P = new TreeNode(0);
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1.left = P;
|
||||
P.left = n2;
|
||||
// Удалить узел P
|
||||
n1.left = n2;
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="binary_tree.cs"
|
||||
/* Вставка и удаление узлов */
|
||||
TreeNode P = new(0);
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1.left = P;
|
||||
P.left = n2;
|
||||
// Удалить узел P
|
||||
n1.left = n2;
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="binary_tree.go"
|
||||
/* Вставка и удаление узлов */
|
||||
// Вставить узел P между n1 -> n2
|
||||
p := NewTreeNode(0)
|
||||
n1.Left = p
|
||||
p.Left = n2
|
||||
// Удалить узел P
|
||||
n1.Left = n2
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="binary_tree.swift"
|
||||
let P = TreeNode(x: 0)
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1.left = P
|
||||
P.left = n2
|
||||
// Удалить узел P
|
||||
n1.left = n2
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="binary_tree.js"
|
||||
/* Вставка и удаление узлов */
|
||||
let P = new TreeNode(0);
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1.left = P;
|
||||
P.left = n2;
|
||||
// Удалить узел P
|
||||
n1.left = n2;
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="binary_tree.ts"
|
||||
/* Вставка и удаление узлов */
|
||||
const P = new TreeNode(0);
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1.left = P;
|
||||
P.left = n2;
|
||||
// Удалить узел P
|
||||
n1.left = n2;
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="binary_tree.dart"
|
||||
/* Вставка и удаление узлов */
|
||||
TreeNode P = new TreeNode(0);
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1.left = P;
|
||||
P.left = n2;
|
||||
// Удалить узел P
|
||||
n1.left = n2;
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="binary_tree.rs"
|
||||
let p = TreeNode::new(0);
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1.borrow_mut().left = Some(p.clone());
|
||||
p.borrow_mut().left = Some(n2.clone());
|
||||
// Удалить узел p
|
||||
n1.borrow_mut().left = Some(n2);
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="binary_tree.c"
|
||||
/* Вставка и удаление узлов */
|
||||
TreeNode *P = newTreeNode(0);
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1->left = P;
|
||||
P->left = n2;
|
||||
// Удалить узел P
|
||||
n1->left = n2;
|
||||
// Освободить память
|
||||
free(P);
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="binary_tree.kt"
|
||||
val P = TreeNode(0)
|
||||
// Вставить узел P между n1 -> n2
|
||||
n1.left = P
|
||||
P.left = n2
|
||||
// Удалить узел P
|
||||
n1.left = n2
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="binary_tree.rb"
|
||||
# Вставка и удаление узлов
|
||||
_p = TreeNode.new(0)
|
||||
# Вставить узел _p между n1 -> n2
|
||||
n1.left = _p
|
||||
_p.left = n2
|
||||
# Удалить узел
|
||||
n1.left = n2
|
||||
```
|
||||
|
||||
??? pythontutor "Визуализация выполнения"
|
||||
|
||||
https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22%D0%9A%D0%BB%D0%B0%D1%81%D1%81%20%D1%83%D0%B7%D0%BB%D0%B0%20%D0%B4%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%BE%D0%B3%D0%BE%20%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B0%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%D0%97%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%83%D0%B7%D0%BB%D0%B0%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D0%B0%20%D0%BD%D0%B0%20%D0%BB%D0%B5%D0%B2%D1%8B%D0%B9%20%D0%B4%D0%BE%D1%87%D0%B5%D1%80%D0%BD%D0%B8%D0%B9%20%D1%83%D0%B7%D0%B5%D0%BB%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D0%B0%20%D0%BD%D0%B0%20%D0%BF%D1%80%D0%B0%D0%B2%D1%8B%D0%B9%20%D0%B4%D0%BE%D1%87%D0%B5%D1%80%D0%BD%D0%B8%D0%B9%20%D1%83%D0%B7%D0%B5%D0%BB%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%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D0%B4%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%BE%D0%B5%20%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D1%83%D0%B7%D0%B5%D0%BB%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B8%D1%82%D1%8C%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%20%D0%BC%D0%B5%D0%B6%D0%B4%D1%83%20%D1%83%D0%B7%D0%BB%D0%B0%D0%BC%D0%B8%20%28%D1%83%D0%BA%D0%B0%D0%B7%D0%B0%D1%82%D0%B5%D0%BB%D0%B8%29%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5%0A%0A%20%20%20%20%23%20%D0%92%D1%81%D1%82%D0%B0%D0%B2%D0%BA%D0%B0%20%D0%B8%20%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%83%D0%B7%D0%BB%D0%BE%D0%B2%0A%20%20%20%20p%20%3D%20TreeNode%280%29%0A%20%20%20%20%23%20%D0%92%D1%81%D1%82%D0%B0%D0%B2%D0%B8%D1%82%D1%8C%20%D1%83%D0%B7%D0%B5%D0%BB%20P%20%D0%BC%D0%B5%D0%B6%D0%B4%D1%83%20n1%20-%3E%20n2%0A%20%20%20%20n1.left%20%3D%20p%0A%20%20%20%20p.left%20%3D%20n2%0A%20%20%20%20%23%20%D0%A3%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C%20%D1%83%D0%B7%D0%B5%D0%BB%20P%0A%20%20%20%20n1.left%20%3D%20n2&cumulative=false&curInstr=37&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
|
||||
|
||||
!!! tip
|
||||
|
||||
Обрати внимание: вставка узла может изменить исходную логическую структуру двоичного дерева, а удаление узла обычно означает удаление этого узла вместе со всеми его поддеревьями. Поэтому в двоичном дереве операции вставки и удаления обычно являются частью более крупного набора операций, который и реализует осмысленное действие.
|
||||
|
||||
## 7.1.3 Распространенные типы двоичных деревьев
|
||||
|
||||
### 1. Идеальное двоичное дерево
|
||||
|
||||
Как показано на рисунке 7-4, <u>идеальное двоичное дерево (perfect binary tree)</u> полностью заполнено на всех уровнях. В идеальном двоичном дереве степень листовых узлов равна $0$ , а у всех остальных узлов степень равна $2$ ; если высота дерева равна $h$ , то общее число узлов равно $2^{h+1} - 1$ , что образует стандартную экспоненциальную зависимость и отражает часто встречающееся в природе явление клеточного деления.
|
||||
|
||||
!!! tip
|
||||
|
||||
Обрати внимание: в китайскоязычном сообществе идеальное двоичное дерево часто называют <u>полностью заполненным двоичным деревом</u>.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-4 Идеальное двоичное дерево </p>
|
||||
|
||||
### 2. Полное двоичное дерево
|
||||
|
||||
Как показано на рисунке 7-5, <u>полное двоичное дерево (complete binary tree)</u> допускает неполное заполнение только на самом нижнем уровне, причем узлы этого уровня должны непрерывно заполняться слева направо. Обрати внимание: идеальное двоичное дерево тоже является полным двоичным деревом.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-5 Полное двоичное дерево </p>
|
||||
|
||||
### 3. Строгое двоичное дерево
|
||||
|
||||
Как показано на рисунке 7-6, <u>строгое двоичное дерево (full binary tree)</u> имеет у всех нелистовых узлов ровно двух дочерних узлов.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-6 Строгое двоичное дерево </p>
|
||||
|
||||
### 4. Сбалансированное двоичное дерево
|
||||
|
||||
Как показано на рисунке 7-7, в <u>сбалансированном двоичном дереве (balanced binary tree)</u> для любого узла абсолютное значение разности высот левого и правого поддеревьев не превышает 1 .
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-7 Сбалансированное двоичное дерево </p>
|
||||
|
||||
## 7.1.4 Вырождение двоичного дерева
|
||||
|
||||
На рисунке 7-8 показаны идеальная структура двоичного дерева и вырожденная структура. Когда каждый уровень двоичного дерева полностью заполнен узлами, мы получаем "идеальное двоичное дерево"; когда же все узлы смещаются к одной стороне, двоичное дерево вырождается в "связный список".
|
||||
|
||||
- Идеальное двоичное дерево соответствует лучшему случаю и позволяет полностью раскрыть преимущества двоичного дерева с точки зрения "разделяй и властвуй".
|
||||
- Связный список представляет противоположную крайность: все операции становятся линейными, а временная сложность деградирует до $O(n)$ .
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-8 Лучший и худший случаи структуры двоичного дерева </p>
|
||||
|
||||
Как показано в таблице 7-1, в лучшем и худшем случаях число листовых узлов, общее число узлов, высота и другие характеристики двоичного дерева достигают максимума или минимума.
|
||||
|
||||
<p align="center"> Таблица 7-1 Лучший и худший случаи структуры двоичного дерева </p>
|
||||
|
||||
<div class="center-table" markdown>
|
||||
|
||||
| | Идеальное двоичное дерево | Связный список |
|
||||
| --------------------------- | ------------------------- | -------------- |
|
||||
| Число узлов на уровне $i$ | $2^{i-1}$ | $1$ |
|
||||
| Число листьев у дерева высоты $h$ | $2^h$ | $1$ |
|
||||
| Общее число узлов у дерева высоты $h$ | $2^{h+1} - 1$ | $h + 1$ |
|
||||
| Высота дерева с $n$ узлами | $\log_2 (n+1) - 1$ | $n - 1$ |
|
||||
|
||||
</div>
|
||||
940
ru/docs/chapter_tree/binary_tree_traversal.md
Normal file
940
ru/docs/chapter_tree/binary_tree_traversal.md
Normal file
@@ -0,0 +1,940 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 7.2 Обход двоичного дерева
|
||||
|
||||
С точки зрения физической структуры дерево представляет собой разновидность структуры данных на основе связей, поэтому его обход выполняется через последовательный доступ к узлам по указателям. Однако дерево является нелинейной структурой данных, а значит, его обход сложнее, чем обход связного списка, и для него требуется использовать поисковые алгоритмы.
|
||||
|
||||
К распространенным способам обхода двоичного дерева относятся обход по уровням, прямой обход, симметричный обход и обратный обход.
|
||||
|
||||
## 7.2.1 Обход по уровням
|
||||
|
||||
Как показано на рисунке 7-9, <u>обход по уровням (level-order traversal)</u> проходит двоичное дерево сверху вниз по уровням и на каждом уровне посещает узлы слева направо.
|
||||
|
||||
По своей сути обход по уровням относится к <u>обходу в ширину (breadth-first traversal)</u>, также называемому <u>поиском в ширину (breadth-first search, BFS)</u>; он отражает идею "расширяться слой за слоем наружу".
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-9 Обход двоичного дерева по уровням </p>
|
||||
|
||||
### 1. Код реализации
|
||||
|
||||
Обход в ширину обычно реализуется с помощью "очереди". Очередь подчиняется правилу "первым пришел - первым вышел", а обход в ширину подчиняется правилу "продвигаться по уровням", поэтому стоящая за ними идея согласована. Код реализации приведен ниже:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="binary_tree_bfs.py"
|
||||
def level_order(root: TreeNode | None) -> list[int]:
|
||||
"""Обход в ширину"""
|
||||
# Инициализировать очередь и добавить корневой узел
|
||||
queue: deque[TreeNode] = deque()
|
||||
queue.append(root)
|
||||
# Инициализировать список для хранения последовательности обхода
|
||||
res = []
|
||||
while queue:
|
||||
node: TreeNode = queue.popleft() # Извлечение из очереди
|
||||
res.append(node.val) # Сохранить значение узла
|
||||
if node.left is not None:
|
||||
queue.append(node.left) # Поместить левый дочерний узел в очередь
|
||||
if node.right is not None:
|
||||
queue.append(node.right) # Поместить правый дочерний узел в очередь
|
||||
return res
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="binary_tree_bfs.cpp"
|
||||
/* Обход в ширину */
|
||||
vector<int> levelOrder(TreeNode *root) {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
queue<TreeNode *> queue;
|
||||
queue.push(root);
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
vector<int> vec;
|
||||
while (!queue.empty()) {
|
||||
TreeNode *node = queue.front();
|
||||
queue.pop(); // Извлечение из очереди
|
||||
vec.push_back(node->val); // Сохранить значение узла
|
||||
if (node->left != nullptr)
|
||||
queue.push(node->left); // Поместить левый дочерний узел в очередь
|
||||
if (node->right != nullptr)
|
||||
queue.push(node->right); // Поместить правый дочерний узел в очередь
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="binary_tree_bfs.java"
|
||||
/* Обход в ширину */
|
||||
List<Integer> levelOrder(TreeNode root) {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
Queue<TreeNode> queue = new LinkedList<>();
|
||||
queue.add(root);
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
List<Integer> list = new ArrayList<>();
|
||||
while (!queue.isEmpty()) {
|
||||
TreeNode node = queue.poll(); // Извлечение из очереди
|
||||
list.add(node.val); // Сохранить значение узла
|
||||
if (node.left != null)
|
||||
queue.offer(node.left); // Поместить левый дочерний узел в очередь
|
||||
if (node.right != null)
|
||||
queue.offer(node.right); // Поместить правый дочерний узел в очередь
|
||||
}
|
||||
return list;
|
||||
}
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="binary_tree_bfs.cs"
|
||||
/* Обход в ширину */
|
||||
List<int> LevelOrder(TreeNode root) {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
Queue<TreeNode> queue = new();
|
||||
queue.Enqueue(root);
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
List<int> list = [];
|
||||
while (queue.Count != 0) {
|
||||
TreeNode node = queue.Dequeue(); // Извлечение из очереди
|
||||
list.Add(node.val!.Value); // Сохранить значение узла
|
||||
if (node.left != null)
|
||||
queue.Enqueue(node.left); // Поместить левый дочерний узел в очередь
|
||||
if (node.right != null)
|
||||
queue.Enqueue(node.right); // Поместить правый дочерний узел в очередь
|
||||
}
|
||||
return list;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="binary_tree_bfs.go"
|
||||
/* Обход в ширину */
|
||||
func levelOrder(root *TreeNode) []any {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
queue := list.New()
|
||||
queue.PushBack(root)
|
||||
// Инициализировать срез для хранения последовательности обхода
|
||||
nums := make([]any, 0)
|
||||
for queue.Len() > 0 {
|
||||
// Извлечение из очереди
|
||||
node := queue.Remove(queue.Front()).(*TreeNode)
|
||||
// Сохранить значение узла
|
||||
nums = append(nums, node.Val)
|
||||
if node.Left != nil {
|
||||
// Поместить левый дочерний узел в очередь
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
if node.Right != nil {
|
||||
// Поместить правый дочерний узел в очередь
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
}
|
||||
return nums
|
||||
}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="binary_tree_bfs.swift"
|
||||
/* Обход в ширину */
|
||||
func levelOrder(root: TreeNode) -> [Int] {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
var queue: [TreeNode] = [root]
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
var list: [Int] = []
|
||||
while !queue.isEmpty {
|
||||
let node = queue.removeFirst() // Извлечение из очереди
|
||||
list.append(node.val) // Сохранить значение узла
|
||||
if let left = node.left {
|
||||
queue.append(left) // Поместить левый дочерний узел в очередь
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right) // Поместить правый дочерний узел в очередь
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="binary_tree_bfs.js"
|
||||
/* Обход в ширину */
|
||||
function levelOrder(root) {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
const queue = [root];
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
const list = [];
|
||||
while (queue.length) {
|
||||
let node = queue.shift(); // Извлечение из очереди
|
||||
list.push(node.val); // Сохранить значение узла
|
||||
if (node.left) queue.push(node.left); // Поместить левый дочерний узел в очередь
|
||||
if (node.right) queue.push(node.right); // Поместить правый дочерний узел в очередь
|
||||
}
|
||||
return list;
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="binary_tree_bfs.ts"
|
||||
/* Обход в ширину */
|
||||
function levelOrder(root: TreeNode | null): number[] {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
const queue = [root];
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
const list: number[] = [];
|
||||
while (queue.length) {
|
||||
let node = queue.shift() as TreeNode; // Извлечение из очереди
|
||||
list.push(node.val); // Сохранить значение узла
|
||||
if (node.left) {
|
||||
queue.push(node.left); // Поместить левый дочерний узел в очередь
|
||||
}
|
||||
if (node.right) {
|
||||
queue.push(node.right); // Поместить правый дочерний узел в очередь
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="binary_tree_bfs.dart"
|
||||
/* Обход в ширину */
|
||||
List<int> levelOrder(TreeNode? root) {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
Queue<TreeNode?> queue = Queue();
|
||||
queue.add(root);
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
List<int> res = [];
|
||||
while (queue.isNotEmpty) {
|
||||
TreeNode? node = queue.removeFirst(); // Извлечение из очереди
|
||||
res.add(node!.val); // Сохранить значение узла
|
||||
if (node.left != null) queue.add(node.left); // Поместить левый дочерний узел в очередь
|
||||
if (node.right != null) queue.add(node.right); // Поместить правый дочерний узел в очередь
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="binary_tree_bfs.rs"
|
||||
/* Обход в ширину */
|
||||
fn level_order(root: &Rc<RefCell<TreeNode>>) -> Vec<i32> {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
let mut que = VecDeque::new();
|
||||
que.push_back(root.clone());
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
let mut vec = Vec::new();
|
||||
|
||||
while let Some(node) = que.pop_front() {
|
||||
// Извлечение из очереди
|
||||
vec.push(node.borrow().val); // Сохранить значение узла
|
||||
if let Some(left) = node.borrow().left.as_ref() {
|
||||
que.push_back(left.clone()); // Поместить левый дочерний узел в очередь
|
||||
}
|
||||
if let Some(right) = node.borrow().right.as_ref() {
|
||||
que.push_back(right.clone()); // Поместить правый дочерний узел в очередь
|
||||
};
|
||||
}
|
||||
vec
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="binary_tree_bfs.c"
|
||||
/* Обход в ширину */
|
||||
int *levelOrder(TreeNode *root, int *size) {
|
||||
/* Вспомогательная очередь */
|
||||
int front, rear;
|
||||
int index, *arr;
|
||||
TreeNode *node;
|
||||
TreeNode **queue;
|
||||
|
||||
/* Вспомогательная очередь */
|
||||
queue = (TreeNode **)malloc(sizeof(TreeNode *) * MAX_SIZE);
|
||||
// Указатель очереди
|
||||
front = 0, rear = 0;
|
||||
// Добавить корневой узел
|
||||
queue[rear++] = root;
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
/* Вспомогательный массив */
|
||||
arr = (int *)malloc(sizeof(int) * MAX_SIZE);
|
||||
// Указатель на массив
|
||||
index = 0;
|
||||
while (front < rear) {
|
||||
// Извлечение из очереди
|
||||
node = queue[front++];
|
||||
// Сохранить значение узла
|
||||
arr[index++] = node->val;
|
||||
if (node->left != NULL) {
|
||||
// Поместить левый дочерний узел в очередь
|
||||
queue[rear++] = node->left;
|
||||
}
|
||||
if (node->right != NULL) {
|
||||
// Поместить правый дочерний узел в очередь
|
||||
queue[rear++] = node->right;
|
||||
}
|
||||
}
|
||||
// Обновить значение длины массива
|
||||
*size = index;
|
||||
arr = realloc(arr, sizeof(int) * (*size));
|
||||
|
||||
// Освободить память вспомогательного массива
|
||||
free(queue);
|
||||
return arr;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="binary_tree_bfs.kt"
|
||||
/* Обход в ширину */
|
||||
fun levelOrder(root: TreeNode?): MutableList<Int> {
|
||||
// Инициализировать очередь и добавить корневой узел
|
||||
val queue = LinkedList<TreeNode?>()
|
||||
queue.add(root)
|
||||
// Инициализировать список для хранения последовательности обхода
|
||||
val list = mutableListOf<Int>()
|
||||
while (queue.isNotEmpty()) {
|
||||
val node = queue.poll() // Извлечение из очереди
|
||||
list.add(node?._val!!) // Сохранить значение узла
|
||||
if (node.left != null)
|
||||
queue.offer(node.left) // Поместить левый дочерний узел в очередь
|
||||
if (node.right != null)
|
||||
queue.offer(node.right) // Поместить правый дочерний узел в очередь
|
||||
}
|
||||
return list
|
||||
}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="binary_tree_bfs.rb"
|
||||
=begin
|
||||
File: binary_tree_bfs.rb
|
||||
Created Time: 2024-04-18
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/tree_node'
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
# ## Обход в ширину ###
|
||||
def level_order(root)
|
||||
# Инициализировать очередь и добавить корневой узел
|
||||
queue = [root]
|
||||
# Инициализировать список для хранения последовательности обхода
|
||||
res = []
|
||||
while !queue.empty?
|
||||
node = queue.shift # Извлечение из очереди
|
||||
res << node.val # Сохранить значение узла
|
||||
queue << node.left unless node.left.nil? # Поместить левый дочерний узел в очередь
|
||||
queue << node.right unless node.right.nil? # Поместить правый дочерний узел в очередь
|
||||
end
|
||||
res
|
||||
end
|
||||
```
|
||||
|
||||
??? pythontutor "Визуализация кода"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=from%20collections%20import%20deque%0A%0Aclass%20TreeNode%3A%0A%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D%2C%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr%2C%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr%2C%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20return%20list_to_tree_dfs%28arr%2C%200%29%0A%0Adef%20level_order%28root%3A%20TreeNode%20%7C%20None%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20queue%3A%20deque%5BTreeNode%5D%20%3D%20deque%28%29%0A%20%20%20%20queue.append%28root%29%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20while%20queue%3A%0A%20%20%20%20%20%20%20%20node%3A%20TreeNode%20%3D%20queue.popleft%28%29%0A%20%20%20%20%20%20%20%20res.append%28node.val%29%0A%20%20%20%20%20%20%20%20if%20node.left%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20queue.append%28node.left%29%0A%20%20%20%20%20%20%20%20if%20node.right%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20queue.append%28node.right%29%0A%20%20%20%20return%20res%0A%27Driver%20Code%27%0Aif%20__name__%20%3D%3D%20%27__main__%27%3A%0A%20%20%20%20root%20%3D%20list_to_tree%28arr%3D%5B1%2C%202%2C%203%2C%204%2C%205%2C%206%2C%207%5D%29%0A%20%20%20%20res%20%3D%20level_order%28root%29%0A%20%20%20%20print%28%27%5Cn%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%B8%20%D1%83%D0%B7%D0%BB%D0%BE%D0%B2%20%D0%BF%D1%80%D0%B8%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B5%20%D0%B2%20%D1%88%D0%B8%D1%80%D0%B8%D0%BD%D1%83%20%3D%20%27%2C%20res%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=127&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=from%20collections%20import%20deque%0A%0Aclass%20TreeNode%3A%0A%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D%2C%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr%2C%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr%2C%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20return%20list_to_tree_dfs%28arr%2C%200%29%0A%0Adef%20level_order%28root%3A%20TreeNode%20%7C%20None%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20queue%3A%20deque%5BTreeNode%5D%20%3D%20deque%28%29%0A%20%20%20%20queue.append%28root%29%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20while%20queue%3A%0A%20%20%20%20%20%20%20%20node%3A%20TreeNode%20%3D%20queue.popleft%28%29%0A%20%20%20%20%20%20%20%20res.append%28node.val%29%0A%20%20%20%20%20%20%20%20if%20node.left%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20queue.append%28node.left%29%0A%20%20%20%20%20%20%20%20if%20node.right%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20queue.append%28node.right%29%0A%20%20%20%20return%20res%0A%27Driver%20Code%27%0Aif%20__name__%20%3D%3D%20%27__main__%27%3A%0A%20%20%20%20root%20%3D%20list_to_tree%28arr%3D%5B1%2C%202%2C%203%2C%204%2C%205%2C%206%2C%207%5D%29%0A%20%20%20%20res%20%3D%20level_order%28root%29%0A%20%20%20%20print%28%27%5Cn%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%B8%20%D1%83%D0%B7%D0%BB%D0%BE%D0%B2%20%D0%BF%D1%80%D0%B8%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B5%20%D0%B2%20%D1%88%D0%B8%D1%80%D0%B8%D0%BD%D1%83%20%3D%20%27%2C%20res%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=127&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>
|
||||
|
||||
### 2. Анализ сложности
|
||||
|
||||
- **Временная сложность равна $O(n)$** : все узлы посещаются по одному разу, поэтому требуется $O(n)$ времени, где $n$ - число узлов.
|
||||
- **Пространственная сложность равна $O(n)$** : в худшем случае, то есть для полной двоичной деревообразной структуры, до достижения самого нижнего уровня в очереди одновременно может находиться до $(n + 1) / 2$ узлов, что требует $O(n)$ памяти.
|
||||
|
||||
## 7.2.2 Прямой, симметричный и обратный обходы
|
||||
|
||||
Соответственно, прямой, симметричный и обратный обходы относятся к <u>обходу в глубину (depth-first traversal)</u>, также называемому <u>поиском в глубину (depth-first search, DFS)</u>; он отражает идею "сначала идти до конца, затем откатываться и продолжать".
|
||||
|
||||
На рисунке 7-10 показан принцип работы обхода двоичного дерева в глубину. **Обход в глубину похож на то, как будто мы обходим всю двоичную структуру по внешнему контуру** , и у каждого узла встречаем три позиции, соответствующие прямому, симметричному и обратному обходам.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-10 Прямой, симметричный и обратный обходы двоичного дерева поиска </p>
|
||||
|
||||
### 1. Код реализации
|
||||
|
||||
Поиск в глубину обычно реализуется через рекурсию:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="binary_tree_dfs.py"
|
||||
def pre_order(root: TreeNode | None):
|
||||
"""Предварительный обход"""
|
||||
if root is None:
|
||||
return
|
||||
# Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
res.append(root.val)
|
||||
pre_order(root=root.left)
|
||||
pre_order(root=root.right)
|
||||
|
||||
def in_order(root: TreeNode | None):
|
||||
"""Симметричный обход"""
|
||||
if root is None:
|
||||
return
|
||||
# Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
in_order(root=root.left)
|
||||
res.append(root.val)
|
||||
in_order(root=root.right)
|
||||
|
||||
def post_order(root: TreeNode | None):
|
||||
"""Обратный обход"""
|
||||
if root is None:
|
||||
return
|
||||
# Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
post_order(root=root.left)
|
||||
post_order(root=root.right)
|
||||
res.append(root.val)
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="binary_tree_dfs.cpp"
|
||||
/* Предварительный обход */
|
||||
void preOrder(TreeNode *root) {
|
||||
if (root == nullptr)
|
||||
return;
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
vec.push_back(root->val);
|
||||
preOrder(root->left);
|
||||
preOrder(root->right);
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
void inOrder(TreeNode *root) {
|
||||
if (root == nullptr)
|
||||
return;
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
inOrder(root->left);
|
||||
vec.push_back(root->val);
|
||||
inOrder(root->right);
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
void postOrder(TreeNode *root) {
|
||||
if (root == nullptr)
|
||||
return;
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
postOrder(root->left);
|
||||
postOrder(root->right);
|
||||
vec.push_back(root->val);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="binary_tree_dfs.java"
|
||||
/* Предварительный обход */
|
||||
void preOrder(TreeNode root) {
|
||||
if (root == null)
|
||||
return;
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
list.add(root.val);
|
||||
preOrder(root.left);
|
||||
preOrder(root.right);
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
void inOrder(TreeNode root) {
|
||||
if (root == null)
|
||||
return;
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
inOrder(root.left);
|
||||
list.add(root.val);
|
||||
inOrder(root.right);
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
void postOrder(TreeNode root) {
|
||||
if (root == null)
|
||||
return;
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
postOrder(root.left);
|
||||
postOrder(root.right);
|
||||
list.add(root.val);
|
||||
}
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="binary_tree_dfs.cs"
|
||||
/* Предварительный обход */
|
||||
void PreOrder(TreeNode? root) {
|
||||
if (root == null) return;
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
list.Add(root.val!.Value);
|
||||
PreOrder(root.left);
|
||||
PreOrder(root.right);
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
void InOrder(TreeNode? root) {
|
||||
if (root == null) return;
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
InOrder(root.left);
|
||||
list.Add(root.val!.Value);
|
||||
InOrder(root.right);
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
void PostOrder(TreeNode? root) {
|
||||
if (root == null) return;
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
PostOrder(root.left);
|
||||
PostOrder(root.right);
|
||||
list.Add(root.val!.Value);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="binary_tree_dfs.go"
|
||||
/* Предварительный обход */
|
||||
func preOrder(node *TreeNode) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
nums = append(nums, node.Val)
|
||||
preOrder(node.Left)
|
||||
preOrder(node.Right)
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
func inOrder(node *TreeNode) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
inOrder(node.Left)
|
||||
nums = append(nums, node.Val)
|
||||
inOrder(node.Right)
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
func postOrder(node *TreeNode) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
postOrder(node.Left)
|
||||
postOrder(node.Right)
|
||||
nums = append(nums, node.Val)
|
||||
}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="binary_tree_dfs.swift"
|
||||
/* Предварительный обход */
|
||||
func preOrder(root: TreeNode?) {
|
||||
guard let root = root else {
|
||||
return
|
||||
}
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
list.append(root.val)
|
||||
preOrder(root: root.left)
|
||||
preOrder(root: root.right)
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
func inOrder(root: TreeNode?) {
|
||||
guard let root = root else {
|
||||
return
|
||||
}
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
inOrder(root: root.left)
|
||||
list.append(root.val)
|
||||
inOrder(root: root.right)
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
func postOrder(root: TreeNode?) {
|
||||
guard let root = root else {
|
||||
return
|
||||
}
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
postOrder(root: root.left)
|
||||
postOrder(root: root.right)
|
||||
list.append(root.val)
|
||||
}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="binary_tree_dfs.js"
|
||||
/* Предварительный обход */
|
||||
function preOrder(root) {
|
||||
if (root === null) return;
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
list.push(root.val);
|
||||
preOrder(root.left);
|
||||
preOrder(root.right);
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
function inOrder(root) {
|
||||
if (root === null) return;
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
inOrder(root.left);
|
||||
list.push(root.val);
|
||||
inOrder(root.right);
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
function postOrder(root) {
|
||||
if (root === null) return;
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
postOrder(root.left);
|
||||
postOrder(root.right);
|
||||
list.push(root.val);
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="binary_tree_dfs.ts"
|
||||
/* Предварительный обход */
|
||||
function preOrder(root: TreeNode | null): void {
|
||||
if (root === null) {
|
||||
return;
|
||||
}
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
list.push(root.val);
|
||||
preOrder(root.left);
|
||||
preOrder(root.right);
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
function inOrder(root: TreeNode | null): void {
|
||||
if (root === null) {
|
||||
return;
|
||||
}
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
inOrder(root.left);
|
||||
list.push(root.val);
|
||||
inOrder(root.right);
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
function postOrder(root: TreeNode | null): void {
|
||||
if (root === null) {
|
||||
return;
|
||||
}
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
postOrder(root.left);
|
||||
postOrder(root.right);
|
||||
list.push(root.val);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="binary_tree_dfs.dart"
|
||||
/* Предварительный обход */
|
||||
void preOrder(TreeNode? node) {
|
||||
if (node == null) return;
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
list.add(node.val);
|
||||
preOrder(node.left);
|
||||
preOrder(node.right);
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
void inOrder(TreeNode? node) {
|
||||
if (node == null) return;
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
inOrder(node.left);
|
||||
list.add(node.val);
|
||||
inOrder(node.right);
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
void postOrder(TreeNode? node) {
|
||||
if (node == null) return;
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
postOrder(node.left);
|
||||
postOrder(node.right);
|
||||
list.add(node.val);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="binary_tree_dfs.rs"
|
||||
/* Предварительный обход */
|
||||
fn pre_order(root: Option<&Rc<RefCell<TreeNode>>>) -> Vec<i32> {
|
||||
let mut result = vec![];
|
||||
|
||||
fn dfs(root: Option<&Rc<RefCell<TreeNode>>>, res: &mut Vec<i32>) {
|
||||
if let Some(node) = root {
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
let node = node.borrow();
|
||||
res.push(node.val);
|
||||
dfs(node.left.as_ref(), res);
|
||||
dfs(node.right.as_ref(), res);
|
||||
}
|
||||
}
|
||||
dfs(root, &mut result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
fn in_order(root: Option<&Rc<RefCell<TreeNode>>>) -> Vec<i32> {
|
||||
let mut result = vec![];
|
||||
|
||||
fn dfs(root: Option<&Rc<RefCell<TreeNode>>>, res: &mut Vec<i32>) {
|
||||
if let Some(node) = root {
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
let node = node.borrow();
|
||||
dfs(node.left.as_ref(), res);
|
||||
res.push(node.val);
|
||||
dfs(node.right.as_ref(), res);
|
||||
}
|
||||
}
|
||||
dfs(root, &mut result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
fn post_order(root: Option<&Rc<RefCell<TreeNode>>>) -> Vec<i32> {
|
||||
let mut result = vec![];
|
||||
|
||||
fn dfs(root: Option<&Rc<RefCell<TreeNode>>>, res: &mut Vec<i32>) {
|
||||
if let Some(node) = root {
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
let node = node.borrow();
|
||||
dfs(node.left.as_ref(), res);
|
||||
dfs(node.right.as_ref(), res);
|
||||
res.push(node.val);
|
||||
}
|
||||
}
|
||||
|
||||
dfs(root, &mut result);
|
||||
|
||||
result
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="binary_tree_dfs.c"
|
||||
/* Предварительный обход */
|
||||
void preOrder(TreeNode *root, int *size) {
|
||||
if (root == NULL)
|
||||
return;
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
arr[(*size)++] = root->val;
|
||||
preOrder(root->left, size);
|
||||
preOrder(root->right, size);
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
void inOrder(TreeNode *root, int *size) {
|
||||
if (root == NULL)
|
||||
return;
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
inOrder(root->left, size);
|
||||
arr[(*size)++] = root->val;
|
||||
inOrder(root->right, size);
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
void postOrder(TreeNode *root, int *size) {
|
||||
if (root == NULL)
|
||||
return;
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
postOrder(root->left, size);
|
||||
postOrder(root->right, size);
|
||||
arr[(*size)++] = root->val;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="binary_tree_dfs.kt"
|
||||
/* Предварительный обход */
|
||||
fun preOrder(root: TreeNode?) {
|
||||
if (root == null) return
|
||||
// Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
list.add(root._val)
|
||||
preOrder(root.left)
|
||||
preOrder(root.right)
|
||||
}
|
||||
|
||||
/* Симметричный обход */
|
||||
fun inOrder(root: TreeNode?) {
|
||||
if (root == null) return
|
||||
// Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
inOrder(root.left)
|
||||
list.add(root._val)
|
||||
inOrder(root.right)
|
||||
}
|
||||
|
||||
/* Обратный обход */
|
||||
fun postOrder(root: TreeNode?) {
|
||||
if (root == null) return
|
||||
// Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
postOrder(root.left)
|
||||
postOrder(root.right)
|
||||
list.add(root._val)
|
||||
}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="binary_tree_dfs.rb"
|
||||
=begin
|
||||
File: binary_tree_dfs.rb
|
||||
Created Time: 2024-04-18
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/tree_node'
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
# ## Предварительный обход ###
|
||||
def pre_order(root)
|
||||
return if root.nil?
|
||||
|
||||
# Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
$res << root.val
|
||||
pre_order(root.left)
|
||||
pre_order(root.right)
|
||||
end
|
||||
|
||||
=begin
|
||||
File: binary_tree_dfs.rb
|
||||
Created Time: 2024-04-18
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/tree_node'
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
# ## Предварительный обход ###
|
||||
def pre_order(root)
|
||||
return if root.nil?
|
||||
|
||||
# Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
$res << root.val
|
||||
pre_order(root.left)
|
||||
pre_order(root.right)
|
||||
end
|
||||
|
||||
# ## Симметричный обход ###
|
||||
def in_order(root)
|
||||
return if root.nil?
|
||||
|
||||
# Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
in_order(root.left)
|
||||
$res << root.val
|
||||
in_order(root.right)
|
||||
end
|
||||
|
||||
=begin
|
||||
File: binary_tree_dfs.rb
|
||||
Created Time: 2024-04-18
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/tree_node'
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
# ## Предварительный обход ###
|
||||
def pre_order(root)
|
||||
return if root.nil?
|
||||
|
||||
# Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
$res << root.val
|
||||
pre_order(root.left)
|
||||
pre_order(root.right)
|
||||
end
|
||||
|
||||
# ## Симметричный обход ###
|
||||
def in_order(root)
|
||||
return if root.nil?
|
||||
|
||||
# Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
in_order(root.left)
|
||||
$res << root.val
|
||||
in_order(root.right)
|
||||
end
|
||||
|
||||
# ## Обратный обход ###
|
||||
def post_order(root)
|
||||
return if root.nil?
|
||||
|
||||
# Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
post_order(root.left)
|
||||
post_order(root.right)
|
||||
$res << root.val
|
||||
end
|
||||
```
|
||||
|
||||
??? pythontutor "Визуализация кода"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=class%20TreeNode%3A%0A%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D%2C%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr%2C%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr%2C%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20return%20list_to_tree_dfs%28arr%2C%200%29%0A%0Adef%20pre_order%28root%3A%20TreeNode%20%7C%20None%29%3A%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20res.append%28root.val%29%0A%20%20%20%20pre_order%28root%3Droot.left%29%0A%20%20%20%20pre_order%28root%3Droot.right%29%0A%0Adef%20in_order%28root%3A%20TreeNode%20%7C%20None%29%3A%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20in_order%28root%3Droot.left%29%0A%20%20%20%20res.append%28root.val%29%0A%20%20%20%20in_order%28root%3Droot.right%29%0A%0Adef%20post_order%28root%3A%20TreeNode%20%7C%20None%29%3A%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20post_order%28root%3Droot.left%29%0A%20%20%20%20post_order%28root%3Droot.right%29%0A%20%20%20%20res.append%28root.val%29%0A%27Driver%20Code%27%0Aif%20__name__%20%3D%3D%20%27__main__%27%3A%0A%20%20%20%20root%20%3D%20list_to_tree%28arr%3D%5B1%2C%202%2C%203%2C%204%2C%205%2C%206%2C%207%5D%29%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20pre_order%28root%29%0A%20%20%20%20print%28%27%5Cn%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%B8%20%D1%83%D0%B7%D0%BB%D0%BE%D0%B2%20%D0%BF%D1%80%D0%B8%20%D0%BF%D1%80%D0%B5%D0%B4%D0%B2%D0%B0%D1%80%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B5%20%3D%20%27%2C%20res%29%0A%20%20%20%20res.clear%28%29%0A%20%20%20%20in_order%28root%29%0A%20%20%20%20print%28%27%5Cn%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%B8%20%D1%83%D0%B7%D0%BB%D0%BE%D0%B2%20%D0%BF%D1%80%D0%B8%20%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D0%BE%D0%BC%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B5%20%3D%20%27%2C%20res%29%0A%20%20%20%20res.clear%28%29%0A%20%20%20%20post_order%28root%29%0A%20%20%20%20print%28%27%5Cn%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%B8%20%D1%83%D0%B7%D0%BB%D0%BE%D0%B2%20%D0%BF%D1%80%D0%B8%20%D0%BE%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%BC%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B5%20%3D%20%27%2C%20res%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=129&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=class%20TreeNode%3A%0A%0A%20%20%20%20def%20__init__%28self%2C%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%0A%0Adef%20list_to_tree_dfs%28arr%3A%20list%5Bint%5D%2C%20i%3A%20int%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20if%20i%20%3C%200%20or%20i%20%3E%3D%20len%28arr%29%20or%20arr%5Bi%5D%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20root%20%3D%20TreeNode%28arr%5Bi%5D%29%0A%20%20%20%20root.left%20%3D%20list_to_tree_dfs%28arr%2C%202%20%2A%20i%20%2B%201%29%0A%20%20%20%20root.right%20%3D%20list_to_tree_dfs%28arr%2C%202%20%2A%20i%20%2B%202%29%0A%20%20%20%20return%20root%0A%0Adef%20list_to_tree%28arr%3A%20list%5Bint%5D%29%20-%3E%20TreeNode%20%7C%20None%3A%0A%20%20%20%20return%20list_to_tree_dfs%28arr%2C%200%29%0A%0Adef%20pre_order%28root%3A%20TreeNode%20%7C%20None%29%3A%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20res.append%28root.val%29%0A%20%20%20%20pre_order%28root%3Droot.left%29%0A%20%20%20%20pre_order%28root%3Droot.right%29%0A%0Adef%20in_order%28root%3A%20TreeNode%20%7C%20None%29%3A%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20in_order%28root%3Droot.left%29%0A%20%20%20%20res.append%28root.val%29%0A%20%20%20%20in_order%28root%3Droot.right%29%0A%0Adef%20post_order%28root%3A%20TreeNode%20%7C%20None%29%3A%0A%20%20%20%20if%20root%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20post_order%28root%3Droot.left%29%0A%20%20%20%20post_order%28root%3Droot.right%29%0A%20%20%20%20res.append%28root.val%29%0A%27Driver%20Code%27%0Aif%20__name__%20%3D%3D%20%27__main__%27%3A%0A%20%20%20%20root%20%3D%20list_to_tree%28arr%3D%5B1%2C%202%2C%203%2C%204%2C%205%2C%206%2C%207%5D%29%0A%20%20%20%20res%20%3D%20%5B%5D%0A%20%20%20%20pre_order%28root%29%0A%20%20%20%20print%28%27%5Cn%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%B8%20%D1%83%D0%B7%D0%BB%D0%BE%D0%B2%20%D0%BF%D1%80%D0%B8%20%D0%BF%D1%80%D0%B5%D0%B4%D0%B2%D0%B0%D1%80%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B5%20%3D%20%27%2C%20res%29%0A%20%20%20%20res.clear%28%29%0A%20%20%20%20in_order%28root%29%0A%20%20%20%20print%28%27%5Cn%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%B8%20%D1%83%D0%B7%D0%BB%D0%BE%D0%B2%20%D0%BF%D1%80%D0%B8%20%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D0%BE%D0%BC%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B5%20%3D%20%27%2C%20res%29%0A%20%20%20%20res.clear%28%29%0A%20%20%20%20post_order%28root%29%0A%20%20%20%20print%28%27%5Cn%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%B8%20%D1%83%D0%B7%D0%BB%D0%BE%D0%B2%20%D0%BF%D1%80%D0%B8%20%D0%BE%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%BC%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B5%20%3D%20%27%2C%20res%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=129&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>
|
||||
|
||||
!!! tip
|
||||
|
||||
Поиск в глубину можно реализовать и итеративно; заинтересованные читатели могут изучить это самостоятельно.
|
||||
|
||||
На рисунках ниже показан рекурсивный процесс прямого обхода двоичного дерева. Его можно разделить на две противоположные части: "вход в рекурсию" и "возврат".
|
||||
|
||||
1. "Вход в рекурсию" означает запуск нового вызова функции; в этом процессе программа переходит к следующему узлу.
|
||||
2. "Возврат" означает завершение вызова функции и возврат назад, то есть текущий узел уже полностью обработан.
|
||||
|
||||
=== "<1>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<2>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<3>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<4>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<5>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<6>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<7>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<8>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<9>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<10>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<11>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 7-11 Рекурсивный процесс прямого обхода </p>
|
||||
|
||||
### 2. Анализ сложности
|
||||
|
||||
- **Временная сложность равна $O(n)$** : все узлы посещаются по одному разу, поэтому требуется $O(n)$ времени.
|
||||
- **Пространственная сложность равна $O(n)$** : в худшем случае, когда дерево вырождается в связный список, глубина рекурсии достигает $n$ , и система тратит $O(n)$ памяти на стек вызовов.
|
||||
23
ru/docs/chapter_tree/index.md
Normal file
23
ru/docs/chapter_tree/index.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
comments: true
|
||||
icon: material/graph-outline
|
||||
---
|
||||
|
||||
# Глава 7. Деревья
|
||||
|
||||
{ class="cover-image" }
|
||||
|
||||
!!! abstract
|
||||
|
||||
Высокое дерево полно жизни: мощные корни, густая крона и раскидистые ветви.
|
||||
|
||||
Оно наглядно показывает нам форму данных, построенную на принципе "разделяй и властвуй".
|
||||
|
||||
## Содержание главы
|
||||
|
||||
- [7.1 Двоичное дерево](binary_tree.md)
|
||||
- [7.2 Обход двоичного дерева](binary_tree_traversal.md)
|
||||
- [7.3 Представление дерева массивом](array_representation_of_tree.md)
|
||||
- [7.4 Двоичное дерево поиска](binary_search_tree.md)
|
||||
- [7.5 AVL-дерево *](avl_tree.md)
|
||||
- [7.6 Резюме](summary.md)
|
||||
58
ru/docs/chapter_tree/summary.md
Normal file
58
ru/docs/chapter_tree/summary.md
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 7.6 Краткие итоги
|
||||
|
||||
### 1. Основные моменты
|
||||
|
||||
- Двоичное дерево - это нелинейная структура данных, отражающая логику "разделения надвое". Каждый узел двоичного дерева содержит значение и два указателя, которые соответственно ведут к левому и правому дочерним узлам.
|
||||
- Для любого узла двоичного дерева дерево, образованное его левым (правым) дочерним узлом и всеми нижележащими узлами, называется левым (правым) поддеревом этого узла.
|
||||
- К связанным с двоичным деревом терминам относятся корневой узел, листовой узел, уровень, степень, ребро, высота, глубина и так далее.
|
||||
- Инициализация двоичного дерева, вставка узлов и удаление узлов похожи по способу реализации на операции со связным списком.
|
||||
- К распространенным видам двоичного дерева относятся идеальное двоичное дерево, полное двоичное дерево, строгое двоичное дерево и сбалансированное двоичное дерево. Идеальное двоичное дерево - наиболее желательное состояние, а связный список - худший случай после вырождения.
|
||||
- Двоичное дерево можно представить массивом: значения узлов и пустые позиции располагаются в порядке обхода по уровням, а связи между родителем и детьми реализуются через отображение индексов.
|
||||
- Обход двоичного дерева по уровням является методом поиска в ширину; он отражает идею "расширяться слой за слоем наружу" и обычно реализуется через очередь.
|
||||
- Прямой, симметричный и обратный обходы относятся к поиску в глубину; они отражают идею "сначала дойти до конца, затем откатиться и продолжить" и обычно реализуются рекурсивно.
|
||||
- Двоичное дерево поиска - это эффективная структура данных для поиска элементов; его поиск, вставка и удаление имеют временную сложность $O(\log n)$ . Когда двоичное дерево поиска вырождается в связный список, все эти сложности деградируют до $O(n)$ .
|
||||
- AVL-дерево, также называемое сбалансированным двоичным деревом поиска, с помощью вращений гарантирует, что после постоянных вставок и удалений узлов дерево остается сбалансированным.
|
||||
- Вращения AVL-дерева включают правое вращение, левое вращение, сначала правое затем левое и сначала левое затем правое. После вставки или удаления узла AVL-дерево выполняет вращения снизу вверх, чтобы снова восстановить баланс.
|
||||
|
||||
### 2. Q & A
|
||||
|
||||
**Q**: Для двоичного дерева, состоящего из одного узла, высота дерева и глубина корня обе равны $0$ ?
|
||||
|
||||
Да, потому что высота и глубина обычно определяются как "число пройденных ребер".
|
||||
|
||||
**Q**: Вставка и удаление в двоичном дереве обычно выполняются в составе набора операций. Что именно означает этот "набор операций"? Можно ли понимать это как освобождение ресурсов у дочерних узлов ресурса?
|
||||
|
||||
Возьмем в качестве примера двоичное дерево поиска: операция удаления узла делится на три случая, и каждый из этих случаев требует нескольких последовательных шагов работы с узлами.
|
||||
|
||||
**Q**: Почему у DFS для двоичного дерева есть три порядка: прямой, симметричный и обратный? Для чего они нужны?
|
||||
|
||||
Подобно прямому и обратному обходу массива, прямой, симметричный и обратный обходы - это три способа обхода двоичного дерева, с помощью которых можно получить результаты в определенном порядке. Например, в двоичном дереве поиска, где соблюдается отношение `значение левого дочернего узла < значение корня < значение правого дочернего узла` , если обходить дерево с приоритетом "лево $\rightarrow$ корень $\rightarrow$ право", то получится упорядоченная последовательность узлов.
|
||||
|
||||
**Q**: Правое вращение работает с отношениями между `node` , `child` и `grand_child` . А связь между `node` и его исходным родителем разве не нужно поддерживать? После правого вращения она ведь не оборвется?
|
||||
|
||||
На это нужно смотреть с точки зрения рекурсии. В правое вращение `right_rotate(root)` передается корень поддерева, а затем через `return child` возвращается корень этого поддерева уже после вращения. Соединение между новым корнем поддерева и его родителем восстанавливается после возврата функции и не входит в обязанности самой операции правого вращения.
|
||||
|
||||
**Q**: В C++ функции делятся на `private` и `public` . Какая логика стоит за этим? Почему `height()` и `updateHeight()` помещают в разные области видимости?
|
||||
|
||||
Главный критерий - область использования метода. Если метод нужен только внутри класса, его следует проектировать как `private` . Например, самостоятельный вызов `updateHeight()` пользователем не имеет смысла: это лишь один из шагов внутри вставки или удаления. А `height()` используется для чтения высоты узла, подобно `vector.size()` , поэтому его разумно делать `public` .
|
||||
|
||||
**Q**: Как построить двоичное дерево поиска из набора входных данных? Важен ли выбор корневого узла?
|
||||
|
||||
Да, важен. Способ построения дерева уже показан в методе `build_tree()` в коде двоичного дерева поиска. Что касается выбора корня, обычно входные данные сортируют, берут средний элемент как корень, а затем рекурсивно строят левое и правое поддеревья. Это позволяет в наибольшей степени сохранить баланс дерева.
|
||||
|
||||
**Q**: Нужно ли в Java всегда использовать `equals()` для сравнения строк?
|
||||
|
||||
В Java для базовых типов `==` используется, чтобы сравнивать, равны ли значения двух переменных. Для ссылочных типов логика у этих двух способов уже разная.
|
||||
|
||||
- `==` : сравнивает, ссылаются ли две переменные на один и тот же объект, то есть совпадает ли их адрес в памяти.
|
||||
- `equals()`: сравнивает, равны ли значения двух объектов.
|
||||
|
||||
Поэтому если нужно сравнить значения, то следует использовать `equals()` . Но строки, инициализированные как `String a = "hi"; String b = "hi";` , хранятся в строковом пуле констант и указывают на один и тот же объект, поэтому в таком случае `a == b` тоже может дать истинный результат при сравнении содержимого.
|
||||
|
||||
**Q**: До достижения самого нижнего уровня при обходе в ширину число узлов в очереди равно $2^h$ ?
|
||||
|
||||
Да. Например, для полного двоичного дерева высоты $h = 2$ общее число узлов равно $n = 7$ , а число узлов на нижнем уровне равно $4 = 2^h = (n + 1) / 2$ .
|
||||
Reference in New Issue
Block a user