Введение

Деревья — это универсальная и широко используемая структура данных в информатике. Они обеспечивают естественный способ представления иерархических отношений и позволяют выполнять эффективные операции поиска, вставки и удаления. В этом сообщении блога мы рассмотрим основы древовидных структур данных в Java, узнаем о некоторых распространенных типах деревьев и посмотрим, как реализовать базовое двоичное дерево.

Понимание деревьев

Дерево — это нелинейная структура данных, состоящая из узлов, соединенных ребрами. Каждый узел содержит значение и ссылки на его дочерние узлы. Деревья обладают следующими свойствами:

  • Единственный корневой узел, от которого происходят все остальные узлы
  • Каждый узел имеет ноль или более дочерних узлов
  • В дереве нет циклов

Некоторая общая терминология дерева включает:

  • Корень: самый верхний узел в дереве
  • Дочерний узел: узел, напрямую связанный с другим узлом (его родителем).
  • Родитель: узел, который имеет один или несколько дочерних узлов.
  • Лист: узел без дочерних элементов
  • Sibling: узлы с одним и тем же родителем

Типы деревьев

Существует несколько типов деревьев, каждый со своими уникальными свойствами и вариантами использования. Некоторые распространенные типы деревьев включают в себя:

  • Двоичные деревья: каждый узел имеет не более двух дочерних элементов, часто называемых левыми и правыми дочерними элементами.
  • Бинарные деревья поиска (BST): бинарное дерево, в котором левый дочерний узел меньше или равен родительскому узлу, а правый дочерний элемент больше родительского узла.
  • AVL Trees: самобалансирующееся бинарное дерево поиска, поддерживающее сбалансированную высоту.
  • Красно-черные деревья: самобалансирующееся бинарное дерево поиска с особыми цветовыми свойствами для поддержания баланса.

Реализация базового двоичного дерева в Java

Чтобы создать бинарное дерево в Java, мы сначала определим класс Node для представления отдельных узлов в дереве:

class Node {
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
        left = null;
        right = null;
    }
}

Теперь мы можем создать класс BinaryTree, который будет содержать корневой узел и предоставлять методы для вставки и обхода узлов:

class BinaryTree {
    Node root;

    public BinaryTree() {
        root = null;
    }

    public void insert(int value) {
        root = insertRecursive(root, value);
    }

    private Node insertRecursive(Node current, int value) {
        if (current == null) {
            return new Node(value);
        }

        if (value < current.value) {
            current.left = insertRecursive(current.left, value);
        } else if (value > current.value) {
            current.right = insertRecursive(current.right, value);
        } else {
            // value already exists, do nothing
            return current;
        }

        return current;
    }
}

В этом простом примере мы реализовали базовое бинарное дерево с методом вставки, который добавляет узлы в отсортированном виде.

Поиск в бинарном дереве

Для поиска значения в бинарном дереве мы добавим метод find в наш класс BinaryTree:

public boolean find(int value) {
    return findRecursive(root, value);
}

private boolean findRecursive(Node current, int value) {
    if (current == null) {
        return false;
    }

    if (value == current.value) {
        return true;
    }

    return value < current.value
        ? findRecursive(current.left, value)
        : findRecursive(current.right, value);
}

Удаление узла в двоичном дереве

Чтобы удалить узел с заданным значением из бинарного дерева, добавим в класс BinaryTree метод delete:

public void delete(int value) {
    root = deleteRecursive(root, value);
}

private Node deleteRecursive(Node current, int value) {
    if (current == null) {
        return null;
    }

    if (value == current.value) {
        // Node to be deleted found
        // Case 1: Node with no children
        if (current.left == null && current.right == null) {
            return null;
        }

        // Case 2: Node with one child
        if (current.left == null) {
            return current.right;
        }

        if (current.right == null) {
            return current.left;
        }

        // Case 3: Node with two children
        int smallestValue = findSmallestValue(current.right);
        current.value = smallestValue;
        current.right = deleteRecursive(current.right, smallestValue);
        return current;
    }

    if (value < current.value) {
        current.left = deleteRecursive(current.left, value);
        return current;
    }

    current.right = deleteRecursive(current.right, value);
    return current;
}

private int findSmallestValue(Node root) {
    return root.left == null ? root.value : findSmallestValue(root.left);
}

Обход бинарного дерева

Существует несколько способов обхода бинарного дерева, включая обход по порядку, в предварительном порядке и в обратном порядке. Здесь мы реализуем обход по порядку, который посещает левое поддерево, текущий узел, а затем правое поддерево:

public void traverseInOrder() {
    traverseInOrderRecursive(root);
    System.out.println();
}

private void traverseInOrderRecursive(Node current) {
    if (current != null) {
        traverseInOrderRecursive(current.left);
        System.out.print(current.value + " ");
        traverseInOrderRecursive(current.right);
    }
}

Заключение

Деревья являются важной структурой данных в информатике, имеющей множество практических приложений в различных областях. В этом сообщении блога мы представили основные концепции деревьев, рассмотрели некоторые распространенные типы деревьев и продемонстрировали, как реализовать простое двоичное дерево в Java с функциями поиска, удаления и обхода. Продолжая свое путешествие с деревьями, вы столкнетесь с более продвинутыми древовидными структурами и алгоритмами, которые помогут эффективно решать сложные задачи.

  1. Древовидные структуры данных — GeeksforGeeks
  2. Двоичные деревья — TutorialsPoint

Понравилось читать? Еще не являетесь участником Medium? Вы можете поддержать мою работу напрямую, зарегистрировавшись по моей реферальной ссылке здесь. Это быстро, просто и не требует дополнительных затрат. Спасибо за вашу поддержку!