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

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

Запись от alhaos размещена 20.05.2026 в 18:57
Показов 1807 Комментарии 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
Комментарии
 
Новые блоги и статьи
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Nekobox - outbounds[0].transport: unknown transport type: raw
damix 01.10.2026
Фикс ошибки Правым кликом по серверу -> отладочная информация -> edit Заменить "net": "raw", на "net": "tcp", Нажать кнопку reload.
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение 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 и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru