Алгоритмы и структуры данных на Go: практическое руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Алгоритмы и структуры данных на Go: от теории к практике

Go (Golang) — это современный компилируемый язык, который сочетает в себе производительность C и простоту Python. Однако, чтобы писать эффективный код на Go, недостаточно знать синтаксис. Понимание алгоритмов и структур данных — ключ к созданию быстрых, масштабируемых приложений. В этой статье мы разберём три фундаментальные темы: бинарный поиск, хеш-таблицы и сортировку, с реальными примерами кода на Go.

1. Бинарный поиск: скорость и точность

Бинарный поиск — это классический алгоритм для поиска элемента в отсортированном массиве. Вместо линейного перебора (O(n)) он сокращает пространство поиска вдвое на каждом шаге, работая за O(log n). В Go это особенно актуально для работы с большими слайсами.

Реализация на Go:

package main

import "fmt"

// BinarySearch возвращает индекс элемента, если он найден, иначе -1
func BinarySearch(arr []int, target int) int {
    left, right := 0, len(arr)-1

    for left <= right {
        mid := left + (right-left)/2 // защита от переполнения

        if arr[mid] == target {
            return mid
        } else if arr[mid] < target {
            left = mid + 1
        } else {
            right = mid - 1
        }
    }
    return -1
}

func main() {
    data := []int{2, 5, 8, 12, 16, 23, 38, 45, 56, 72}
    target := 23
    index := BinarySearch(data, target)
    fmt.Printf("Индекс элемента %d: %d\n", target, index) // Вывод: Индекс элемента 23: 5
}

Практические советы:

  • Всегда проверяйте, что массив отсортирован. В Go можно использовать sort.Ints() из пакета sort.
  • Формула mid := left + (right-left)/2 предотвращает целочисленное переполнение при больших массивах.
  • Для работы со строками используйте sort.SearchStrings() из стандартной библиотеки.

2. Хеш-таблицы: быстрый доступ к данным

В Go встроенная хеш-таблица — это map. Она обеспечивает доступ к элементам за O(1) в среднем. Но важно понимать, как она работает под капотом, чтобы избежать "подводных камней". Хеш-таблица использует хеш-функцию для преобразования ключа в индекс массива (bucket). Коллизии решаются методом цепочек (отдельные списки для каждого bucket).

Пример работы с map:

package main

import "fmt"

func main() {
    // Создание map
    scores := make(map[string]int)
    scores["Анна"] = 95
    scores["Борис"] = 87
    scores["Виктор"] = 92

    // Поиск с проверкой существования
    name := "Борис"
    if score, exists := scores[name]; exists {
        fmt.Printf("Оценка %s: %d\n", name, score)
    } else {
        fmt.Printf("Студент %s не найден\n", name)
    }

    // Удаление
    delete(scores, "Анна")

    // Итерация (порядок не гарантирован!)
    for key, value := range scores {
        fmt.Printf("%s: %d\n", key, value)
    }
}

Важные моменты:

  • Map в Go — это ссылочный тип. При передаче в функцию изменения отразятся на оригинале.
  • Не используйте nil map для записи — это вызовет панику. Всегда инициализируйте через make().
  • Порядок итерации по map не детерминирован. Если нужен порядок — используйте отдельный слайс ключей.
  • Для ключей можно использовать только сравнимые типы: числа, строки, булевы, указатели, структуры без слайсов/map.

3. Сортировка: от пузырька до быстрой

Go предлагает мощный пакет sort, который реализует быструю сортировку (quicksort) и другие алгоритмы. Но для учебных целей и понимания важно написать сортировку вручную. Рассмотрим две реализации: пузырьковую (O(n²)) для маленьких данных и быструю (O(n log n)) для больших.

Пузырьковая сортировка:

func BubbleSort(arr []int) {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        swapped := false
        for j := 0; j < n-i-1; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = true
            }
        }
        // Если не было обменов — массив уже отсортирован
        if !swapped {
            break
        }
    }
}

Быстрая сортировка (QuickSort):

func QuickSort(arr []int) {
    if len(arr) < 2 {
        return
    }

    // Опорный элемент — середина
    pivot := arr[len(arr)/2]
    left, right := 0, len(arr)-1

    for left <= right {
        for arr[left] < pivot {
            left++
        }
        for arr[right] > pivot {
            right--
        }
        if left <= right {
            arr[left], arr[right] = arr[right], arr[left]
            left++
            right--
        }
    }

    // Рекурсивно сортируем левую и правую части
    QuickSort(arr[:right+1])
    QuickSort(arr[left:])
}

Когда что использовать:

  • Пузырёк — только для обучения или массивов до 100 элементов.
  • Быстрая — универсальный выбор. В Go используется в sort.Slice().
  • sort.Ints() — для production-кода. Стандартная библиотека оптимизирована под конкретные типы.

4. Деревья: бинарное дерево поиска (BST)

Деревья — это иерархические структуры данных. Бинарное дерево поиска (BST) позволяет выполнять вставку, поиск и удаление за O(log n) в среднем. В Go это реализуется через указатели и структуры.

Реализация BST на Go:

package main

import "fmt"

// Node — узел дерева
type Node                

Блоги

Рекомендаций по книгам