Двоичные деревья: виды и спецификация

Модуль "Деревья" • урок 2 из 11 • ~6 минут

Суть урока. Двоичное (бинарное) дерево - это не просто дерево, у которого "выросло не больше двух ветвей". Это позиционное дерево, которое либо пусто, либо состоит из корня и строго двух позиций: левого и правого поддерева. Наклон ветвей имеет принципиальное значение.
Двоичные деревья и позиционность: дерево с одним левым сыном и дерево с одним правым сыном - разные
Пустая позиция тоже часть структуры: левый сын и правый сын - разные вещи

Отличие от деревьев общего вида

Бинарное (двоичное) дерево - это конечное множество узлов, которое или пусто, или состоит из корня и двух непересекающихся поддеревьев (левого и правого).

Два важнейших отличия бинарного дерева от дерева общего вида:

  1. В бинарном дереве принципиальное значение имеет наклон ветвей (позиционность). Дерево с одним левым сыном и дерево с одним правым сыном - это два абсолютно разных бинарных дерева (в то время как в теории деревьев общего вида они были бы идентичны, так как это просто "один сын").
  2. Бинарное дерево по определению может быть пустым. Дерево общего вида всегда содержит хотя бы один корень.

Двоичные деревья: строгие, полные и совершенные

В программировании и анализе алгоритмов выделяют три важных подвида двоичных деревьев:

Три подвида двоичных деревьев: строгое, полное и совершенное
Строгое, полное и совершенное - три подвида с разными ограничениями

Функциональная спецификация

Чтобы абстрагироваться от конкретного языка программирования, дерево описывается через функциональную спецификацию - набор математических операций (что оно умеет делать), без привязки к тому, как это физически лежит в оперативной памяти.

Базовый набор операций спецификации:

Для этих операций задаются логические аксиомы:

Обратите внимание: абстрактная функциональная спецификация дерева принципиально не описывает вставку или удаление одиночных узлов "на лету" - она работает с деревом как с единым целым объектом (комбинируя или расщепляя поддеревья). Вставка узла зависит от конкретного алгоритма (например, двоичного дерева поиска), о котором мы поговорим в следующем уроке.

Интерфейс на C

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

#include <stdbool.h>

typedef int Data;
typedef struct Tree Tree; // Скрытая (непрозрачная) реализация структуры

// Интерфейс, отражающий функциональную спецификацию:
Tree* MakeTree(Data v, Tree* left, Tree* right); 
bool IsEmpty(Tree* t);                           
Data GetRootData(Tree* t);                       
Tree* GetLeftSubtree(Tree* t);                   
Tree* GetRightSubtree(Tree* t);                  
void DestroyTree(Tree* t);

Коротко: главное

  • Двоичное дерево - позиционное дерево (допускает пустоту, левый узел отличается от правого).
  • Виды: Строгое (нет одиноких сыновей), Полное (заполнено плотно слева направо), Совершенное (идеальный треугольник).
  • Спецификация: Задает операции сборки (СОЗДАТЬ) и навигации (КОРЕНЬ, ЛЕВОЕ, ПРАВОЕ). Работает с поддеревьями, а не с вставками одиночных узлов.

Проверьте себя

Какой вид двоичного дерева требует, чтобы последний уровень заполнялся строго слева направо без "дырок"?

Как вам урок? Спасибо за отзыв!