TGViewer
Библиотека Go-разработчика | Golang Библиотека Go-разработчика | Golang @goproglib · 24.1K subscribers
Post #7266 2.83K
🌴 Деревья поиска на Go: вставка, поиск, удаление

BST — двоичное дерево поиска. Простая идея: левый потомок всегда меньше родителя, правый — больше. Это даёт поиск за O(log n), но только если дерево сбалансировано. Об этом — в конце поста.

Структура узла

type Node struct {
Val int
Left *Node
Right *Node
}

type BST struct {
Root *Node
}


Вставка

Идём по дереву вниз: если значение меньше текущего узла — налево, больше — направо. Когда упёрлись в nil — вставляем:
func (t *BST) Insert(val int) {
t.Root = insert(t.Root, val)
}

func insert(node *Node, val int) *Node {
if node == nil {
return &Node{Val: val}
}
if val < node.Val {
node.Left = insert(node.Left, val)
} else if val > node.Val {
node.Right = insert(node.Right, val)
}
return node
}


Дубликаты здесь просто игнорируются. Можно добавить счётчик в узел, если нужно их хранить.

Поиск

Та же логика, что и вставка, но вместо создания узла возвращаем результат:
func (t *BST) Search(val int) bool {
return search(t.Root, val)
}

func search(node *Node, val int) bool {
if node == nil {
return false
}
if val == node.Val {
return true
}
if val < node.Val {
return search(node.Left, val)
}
return search(node.Right, val)
}


Удаление

Три случая:
- узел без потомков — просто удаляем
- узел с одним потомком — заменяем им
- узел с двумя потомками — находим минимальный элемент правого поддерева (in-order successor), ставим его на место удалённого

func (t *BST) Delete(val int) {
t.Root = deleteNode(t.Root, val)
}

func deleteNode(node *Node, val int) *Node {
if node == nil {
return nil
}
if val < node.Val {
node.Left = deleteNode(node.Left, val)
} else if val > node.Val {
node.Right = deleteNode(node.Right, val)
} else {
if node.Left == nil {
return node.Right
}
if node.Right == nil {
return node.Left
}
// Находим минимум в правом поддереве
min := findMin(node.Right)
node.Val = min.Val
node.Right = deleteNode(node.Right, min.Val)
}
return node
}

func findMin(node *Node) *Node {
for node.Left != nil {
node = node.Left
}
return node
}


Обходы

Три классических варианта, каждый даёт узлы в разном порядке:
// In-order: левый → корень → правый (отдаёт отсортированный список)
func inOrder(node *Node) {
if node == nil {
return
}
inOrder(node.Left)
fmt.Println(node.Val)
inOrder(node.Right)
}

// Pre-order: корень → левый → правый (удобен для копирования дерева)
func preOrder(node *Node) {
if node == nil {
return
}
fmt.Println(node.Val)
preOrder(node.Left)
preOrder(node.Right)
}

// Post-order: левый → правый → корень (удобен для удаления дерева)
func postOrder(node *Node) {
if node == nil {
return
}
postOrder(node.Left)
postOrder(node.Right)
fmt.Println(node.Val)
}


Где ломается BST

Если вставлять элементы по порядку — 1, 2, 3, 4, 5 — дерево вырождается в связный список. Поиск становится O(n) вместо O(log n):
1
\
2
\
3
\
4
\
5


Именно для этого придумали самобалансирующиеся деревья.

💬 Разбирать механизм, который не даёт дереву деградировать?

📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max

🐸 Библиотека Go-разработчика

#ReadySetGo
  • 👍 19
  • ❤ 2
  • 🔥 2
More from @goproglib
  1. Sep 29, 2026👨‍💻 Библиотека для написания LSP-серверов Написать свой Language Server с нуля на Go сло…
  2. Sep 28, 2026🤔 Вопрос с собеседования по Go Что выведет программа? ❤️ — 1 true / 0 false 🔥 — 1 true /…
  3. Sep 28, 2026👩‍💻 Что на самом деле происходит внутри Go map? После Go 1.24 обычный map внутри работае…
  4. Sep 26, 2026🔥 В Go 1.27 появился portable SIMD До этого SIMD-оптимизации в Go требовали архитектурног…
  5. Sep 25, 2026🤡🤡 📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека Go-разработчика #GoGiggle
  6. Sep 25, 2026💡 Код работает. А data race уже есть В Go можно записать значение в одной горутине, прочи…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →