Форум программистов, компьютерный форум, киберфорум
alhaos
Войти
Регистрация
Восстановить пароль

[golang] Двоичная куча, min-heap

Запись от alhaos размещена 20.05.2026 в 18:57
Показов 1783 Комментарии 0
Метки datastructure, golang

Двоичная куча



Двоичная куча — структура данных, которая всегда держит самый важный элемент наготове.

Представьте очередь к хилеру в игре, и очередь из игроков в приоритете те у кого меньше всего HP.

Как она устроена внутри

Куча хранится в обычном массиве, но если смотреть на неё как на дерево — каждый узел имеет двух детей. Отсюда название: двоичная (два ребёнка) куча.

Главное правило — каждый родитель меньше своих детей (в min-heap). Это значит, что самый маленький элемент всегда сидит в корне — на нулевом индексе массива. Не где-то в середине, не в конце — всегда в начале.

Реализация на го:



Go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package binaryHeap
 
import "errors"
 
var ErrEmptyHeap = errors.New("heap is empty")
 
// Интерфейс двоичной кучи
type BinaryHeap interface {
    Push(n int)
    Pop() (int, error)
    Peek() (int, error)
    Len() int
    IsEmpty() bool
}
 
// binaryHeap структура двоичной кучи
type binaryHeap struct {
    data []int
}
 
// NewBinaryHeap возвращает новый экземпляр структуры BinaryHeap
func NewBinaryHeap() BinaryHeap {
    return &binaryHeap{
        data: make([]int, 0, 16),
    }
}
 
// Push добавляет элемент в двоичную кучу
func (h *binaryHeap) Push(n int) {
 
    // Добавляем в конец массива элемент
    h.data = append(h.data, n)
 
    // Фиксируем индекс элемента для просеивания вверх
    currentIndex := len(h.data) - 1
 
    // Цикл к корню кучи
    for currentIndex > 0 {
 
        // Находим индекс родительского элемента
        parentIndex := (currentIndex - 1) / 2
 
        // Если родительский элемент больше
        if h.data[parentIndex] > h.data[currentIndex] {
 
            // Меняем местами с текущим
            h.data[parentIndex], h.data[currentIndex] = h.data[currentIndex], h.data[parentIndex]
 
            // Изменяем индекс текущего элемента на индекс родительского элемента
            currentIndex = parentIndex
        } else { // Если родительский элемент не больше
            break // прерываем цикл
        }
    }
}
 
// Pop извлекает корневой элемент из бинарной кучи
func (h *binaryHeap) Pop() (int, error) {
 
    // Если куча пуста возвращаем 0 и ошибку
    if len(h.data) == 0 {
        return 0, ErrEmptyHeap
    }
 
    // Забираем элемент для возврата
    returnValue := h.data[0]
 
    // На его место вставляем крайний элемент
    h.data[0] = h.data[len(h.data)-1]
 
    // Обрезаем слайс
    h.data = h.data[:len(h.data)-1]
 
    // Устанавливаем 0 как индекс просматриваемого элемента
    currentIndex := 0
 
    // Просеиваем вниз
    for {
        leftChildIndex := 2*currentIndex + 1
        if leftChildIndex >= len(h.data) {
            break
        }
        rightChildIndex := 2*currentIndex + 2
 
        // выбираем наименьшего ребёнка
        smallestIndex := leftChildIndex
        if rightChildIndex < len(h.data) && h.data[rightChildIndex] < h.data[leftChildIndex] {
            smallestIndex = rightChildIndex
        }
 
        // если текущий элемент уже меньше — стоп
        if h.data[currentIndex] <= h.data[smallestIndex] {
            break
        }
 
        h.data[currentIndex], h.data[smallestIndex] = h.data[smallestIndex], h.data[currentIndex]
        currentIndex = smallestIndex
    }
 
    return returnValue, nil
}
 
// Peek Возвращает элемент в корне кучи
func (h *binaryHeap) Peek() (int, error) {
    if len(h.data) == 0 {
        return 0, ErrEmptyHeap
    }
    return h.data[0], nil
}
 
// Len возвращает длину двоичной кучи
func (h *binaryHeap) Len() int {
    return len(h.data)
}
 
// IsEmpty возвращает true если куча пуста, иначе false
func (h *binaryHeap) IsEmpty() bool {
    return len(h.data) == 0
}

Тесты:



Go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package binaryHeap
 
import (
    "errors"
    "testing"
)
 
func TestPeekOnEmptyHeap(t *testing.T) {
    h := NewBinaryHeap()
    _, err := h.Peek()
    if err == nil {
        t.Error("expected error on empty heap")
    }
}
 
func TestPopOnEmptyHeap(t *testing.T) {
    h := NewBinaryHeap()
    _, err := h.Pop()
    if err == nil {
        t.Error("expected error on empty heap")
    }
}
 
func TestIsEmptyOnNewHeap(t *testing.T) {
    h := NewBinaryHeap()
    if !h.IsEmpty() {
        t.Error("expected new heap to be empty")
    }
}
 
func TestLenOnNewHeap(t *testing.T) {
    h := NewBinaryHeap()
    if h.Len() != 0 {
        t.Errorf("expected len 0, got %d", h.Len())
    }
}
 
func TestPushSingleElement(t *testing.T) {
    h := NewBinaryHeap()
    h.Push(42)
 
    if h.IsEmpty() {
        t.Error("expected heap to be non-empty after Push")
    }
    if h.Len() != 1 {
        t.Errorf("expected len 1, got %d", h.Len())
    }
 
    val, err := h.Peek()
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if val != 42 {
        t.Errorf("expected 42, got %d", val)
    }
}
 
func TestPushMultipleElementsPeekReturnsMin(t *testing.T) {
    h := NewBinaryHeap()
    h.Push(30)
    h.Push(10)
    h.Push(20)
 
    val, err := h.Peek()
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if val != 10 {
        t.Errorf("expected min 10, got %d", val)
    }
}
 
func TestPopReturnsSortedOrder(t *testing.T) {
    h := NewBinaryHeap()
    input := []int{50, 10, 40, 20, 30}
    for _, v := range input {
        h.Push(v)
    }
 
    expected := []int{10, 20, 30, 40, 50}
    for _, want := range expected {
        got, err := h.Pop()
        if err != nil {
            t.Fatalf("unexpected error: %v", err)
        }
        if got != want {
            t.Errorf("expected %d, got %d", want, got)
        }
    }
}
 
func TestPopDecreasesLen(t *testing.T) {
    h := NewBinaryHeap()
    h.Push(1)
    h.Push(2)
    h.Push(3)
 
    h.Pop()
    if h.Len() != 2 {
        t.Errorf("expected len 2, got %d", h.Len())
    }
}
 
func TestIsEmptyAfterPopAll(t *testing.T) {
    h := NewBinaryHeap()
    h.Push(1)
    h.Pop()
 
    if !h.IsEmpty() {
        t.Error("expected heap to be empty after popping all elements")
    }
}
 
func TestPeekDoesNotRemoveElement(t *testing.T) {
    h := NewBinaryHeap()
    h.Push(5)
 
    h.Peek()
    if h.Len() != 1 {
        t.Errorf("expected len 1 after Peek, got %d", h.Len())
    }
}
 
func TestErrorType(t *testing.T) {
    h := NewBinaryHeap()
    _, err := h.Pop()
    if !errors.Is(err, ErrEmptyHeap) {
        t.Errorf("expected ErrEmptyHeap, got %v", err)
    }
}
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
go test -v
=== RUN   TestPeekOnEmptyHeap
--- PASS: TestPeekOnEmptyHeap (0.00s)
=== RUN   TestPopOnEmptyHeap
--- PASS: TestPopOnEmptyHeap (0.00s)
=== RUN   TestIsEmptyOnNewHeap
--- PASS: TestIsEmptyOnNewHeap (0.00s)
=== RUN   TestLenOnNewHeap
--- PASS: TestLenOnNewHeap (0.00s)
=== RUN   TestPushSingleElement
--- PASS: TestPushSingleElement (0.00s)
=== RUN   TestPushMultipleElementsPeekReturnsMin
--- PASS: TestPushMultipleElementsPeekReturnsMin (0.00s)
=== RUN   TestPopReturnsSortedOrder
--- PASS: TestPopReturnsSortedOrder (0.00s)
=== RUN   TestPopDecreasesLen
--- PASS: TestPopDecreasesLen (0.00s)
=== RUN   TestIsEmptyAfterPopAll
--- PASS: TestIsEmptyAfterPopAll (0.00s)
=== RUN   TestPeekDoesNotRemoveElement
--- PASS: TestPeekDoesNotRemoveElement (0.00s)
=== RUN   TestErrorType
--- PASS: TestErrorType (0.00s)
PASS
ok      github.com/alhaos/problems/dataStructures/binaryHeap    0.338s
Метки datastructure, golang
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru