You are on page 1of 51

Лекция 4

Деревья и графы
Я попыталась сфокусировать взгляд на трубке лампы у себя над головой. Что-то случилось,
я знала это, но тот вечер, когда мы с Тэмвортом попытались взять Ахерона Аида, стерся из
моей памяти. По крайней мере, на данный момент. Я нахмурилась: в моем сознании
мелькали лишь обрывочные картинки. Я вспомнила, как трижды стреляла в маленькую
старушку, а затем поспешно спускалась по ржавой пожарной лестнице. Я смутно помнила,
как стреляла в собственную машину, а потом получила пулю в руку… Посмотрела на руку –
она и правда была туго перевязана белым бинтом. Затем вспомнила, что в меня попали еще
раз – в грудь. Я пару раз глубоко вдохнула и выдохнула – и с облегчением поняла, что не
слышу хрипов. В комнате была сиделка, которая сказала мне несколько слов – я не
разобрала их – и улыбнулась. Это показалось мне странным, и я снова погрузилась в
милосердное беспамятство.
Дж. ФФОРДЕ, Дело Джен, или Эйра немилосердия, Гл. 5 Бей своих, чтоб чужие боялись
Деревья
• Дерево каталогов
• Иерархия организации
• Структура программы
• …
Деревья (пример)

Здание
Этажи Этажи Этажи Этажи
Комнаты
Оборудование
Деревья (структура)
Название Название типа потомков
Потомок 1 Потомок 2 Потомок 3

Название Название типа потомков Название Название типа потомков


Потомок 1 Потомок 2 Потомок 3 Потомок 1 Потомок 2 Потомок 3
Деревья (структура)
Таллинская 34, учебн. Этаж 5

Первый Комната 25 …

101 12 … 102 24 …

Принтер НР LJ S/N 123456 0 Ноутбук S/N 123457 0


Деревья (Класс)
class EquipmentTree
{public:
// Здесь д.б. 4 конструктора и деструктор
int getChildCount() const;
void AddChild(const char*);
void delChild(int);
EquipmentTree &getChild(int);
// Возвращает потомка по его имени.
EquipmentTree &getChild(const char *);
Деревья (Класс)
public:
void setInfoString(const char*);
char *getInfoString();
void setChildString(const char*);
char *getChildString();
// Возвращает оборудование – массив строк.
// cnt возвращает размер списка.
char **getEquipmentList(int &cnt);
// Возвращаем оборудование в заданной
единице аналогично предыдущей функции.
char **getEquipmentList(char **, int&);
Деревья (Класс)
private:
EquipmentTree *childs;
int childCount;
char *infoString;
char *childString;
};
Обход дерева
Если нет потомков
слить infoString и childString
вернуть одну строку
Иначе
рекурсивно вызвать получение списка для
всех потомков
добавить к началу строк свою InfoString
вернуть объединенный список
Обход дерева
char ** EquipmentTree::
getEquipmentList(int &cnt)
{char **res;
if( !childCount )
{res=new char*[1];
res[0] = new char[strlen(infoString) +
strlen(childString) + 1];
// Допустим, у нас есть такая функция.
merge(res[0], infoString, childString);
cnt=1;
return res;
}
Обход дерева
else
{char **tmp;
int c;
res=null;
cnt=0;
for( auto i=0; i<childCount; i++ )
{tmp=childs[i].getEquipmentList(c);
mergeLists(res, cnt, tmp, c);
appendString(res, cnt, childString);
}
return res; } }
Обход дерева
Принтер НР LJ 123456
Таллинская 34, учебн. Этаж
Ноутбук 123457
Ноутбук 123458
Первый Комната Второй Комната Ноутбук 123459

101 125

Принтер НР LJ Ноутбук Принтер НР LJ Компьютер


S/N 123456 S/N 123458 S/N 123550 S/N 123551

Ноутбук Ноутбук Роутер Компьютер


S/N 123457 S/N 123459 S/N 123552 S/N 123553
Обход дерева
101 Принтер НР LJ 123456
Таллинская 34, учебн. Этаж
101 Ноутбук 123457
101 Ноутбук 123458
Первый Комната Второй Комната 101 Ноутбук 123459

101 125

Принтер НР LJ Ноутбук Принтер НР LJ Компьютер


S/N 123456 S/N 123458 S/N 123550 S/N 123551

Ноутбук Ноутбук Роутер Компьютер


S/N 123457 S/N 123459 S/N 123552 S/N 123553
Обход дерева
101 Принтер НР LJ 123456
Таллинская 34, учебн. Этаж
101 Ноутбук 123457
101 Ноутбук 123458
Первый Комната Второй Комната 101 Ноутбук 123459
125 Принтер НР LJ 123550
125 Ноутбук 123551
101 125 125 Ноутбук 123552
125 Ноутбук 123553

Принтер НР LJ Ноутбук Принтер НР LJ Компьютер


S/N 123456 S/N 123458 S/N 123550 S/N 123551

Ноутбук Ноутбук Роутер Компьютер


S/N 123457 S/N 123459 S/N 123552 S/N 123553
Обход дерева
Первый комн. 101 Принтер НР LJ 123456
Таллинская 34, учебн. Этаж
Первый комн. 101 Ноутбук 123457

Первый Комната Второй Комната
Первый комн. 125 Принтер НР LJ 123550
Первый комн. 125 Ноутбук 123551
101 125 …

Принтер НР LJ Ноутбук Принтер НР LJ Компьютер


S/N 123456 S/N 123458 S/N 123550 S/N 123551

Ноутбук Ноутбук Роутер Компьютер


S/N 123457 S/N 123459 S/N 123552 S/N 123553
Прямой обход дерева
Вывести себя
Вывести всех потомков
Прямой обход дерева
void EquipmentTree::getEquipmentList()
{cout<<infoString;
if( childsCount==0)
{cout<<“ “<<childString;
return;
}
for(auto i=0; i<childsCount; i++)
{cout<<“;“<<childString<<“ “;
childs[i].getEquipmentList();
}
}
Прямой обход дерева
Таллинская 34, учебн.; Этаж Первый; Комната
101 Принтер HP LJ 123456; Ноутбук 123457;
Ноутбук 123458; Ноутбук 123459; Комната 102
…;
Комната 125 Принтер HP LJ 123550; Ноутбук
123551; Ноутбук 123552; Ноутбук 123553; Этаж
Второй; Комната 201 …
Прямая польская запись
Вывести себя [Вывести всех потомков]
Прямая польская запись
A [B, C [D, E], F [G [H]]]

Что здесь записано за дерево?

B C F

D E G

H
Обратный обход дерева
void EquipmentTree::getEquipmentList()
{if( childsCount==0)
{cout<<infoString<<“ “<<childString;
return;
}
for(auto i=0; i<childsCount; i++)
{childs[i].getEquipmentList();
cout<<childString<<“ “<<“;”;
}
cout<<infoString;
}
Обратный обход дерева
Принтер HP LJ 123456; Ноутбук 123457;
Ноутбук 123458; Ноутбук 123459; Комната 101;
…;
Принтер HP LJ 123550; Ноутбук 123551;
Ноутбук 123552; Ноутбук 123553; Комната 125;
Этаж Первый; …; Комната 201;…; Этаж Второй;
… ; Таллинская 34, учебн.
Обратная польская запись
[B, [D, E] C, [[H] G] F] A

Что здесь записано за дерево?

B C F

D E G

H
Деревья
(фиксированная структура)

Данные Первый потомок Правый брат


Деревья
(фиксированная структура)
A

B C F

D E G

Удобно хранить в базе данных.


Удобно использовать если надо фиксировать
затраты памяти.
Задача
Требуется хранить словарь слов большого
размера и обеспечивать эффективный поиск по
нему.
Решение
решение (массив слов).
Решение
АБАЖУР
АБАК
АББАТ
АББАТИСА
АББРЕВИАТУРА
АБВЕР
АБДУКЦИЯ
АБЗАЦ

Решение
Отвратительное решение (массив слов).
Скорость поиска:
lg(размер словаря)*длина слова/2
Объем памяти:
Объем словаря
Скорость добавления:
Сдвинуть половину словаря (указатели)
Двоичное дерево
Узел хранит данные и указатели на правого и
левого потомков.
Если искомые данные больше данных, хранимых
в вершине, они хранятся в правом потомке.
Если искомые данные меньше данных, хранимых
в вершине, они хранятся в левом потомке.

Левый потомок Данные Правый потомок


Двоичное дерево
КОРАЛЛ

ЕЖИК ПЕЧЕНЕГ

ЗАПОЛЯРЬЕ МАРКЕР ТОПЛИВО

ЖАБРЫ ИСКРА НОВОСТЬ


Решение
Плохое решение (дерево слов).
Скорость поиска:
lg(размер словаря)*длина слова/2
Объем памяти:
Объем словаря+2*размер словаря
Скорость добавления:
Поиск + добавление одного элемента
Балансирование
двоичного дерева
Если длина пути от текущей вершины до самого
глубокого левого потомка отличается от длины пути от
текущей вершины до самого глубокого правого
потомка, то поиск в правом и левом поддеревьях будут
различаться. В самом плохом случае, когда на вход
поступает отсортированная последовательность,
дерево будет построено как единый путь от первой до
последней вершины. Скорость поиска в таком дереве
пропорциональна количеству элементов.
Для устранения этого недостатка необходимо
сбалансировать дерево.
Две стратегии
балансирования
1. Красно-черные деревья
2. АВЛ-деревья
Красно-черные деревья
• Узел либо красный, либо чёрный.
• Корень — чёрный. (В других определениях это
правило иногда опускается. Это правило слабо
влияет на анализ, так как корень всегда может быть
изменен с красного на чёрный, но не обязательно
наоборот).
• Все листья (NIL) — чёрные.
• Оба потомка каждого красного узла — чёрные.
• Всякий простой путь от данного узла до любого
листового узла, являющегося его потомком,
содержит одинаковое число чёрных узлов.
Красно-черные деревья
АВЛ-деревья
Малое левое вращение Малое правое вращение
АВЛ-деревья
Большое левое вращение Большое правое вращение
B-дерево
Каждая вершина хранит от N-1 до 2*N-1
элемента данных и указывает на одного больше
потомков.
Если искомые данные меньше текущего
элемента, следует перейти к предыдущему
потомку. В противном случае необходимо идти
дальше.
Потомок1 Данные1 Потомок2 Данные2 Потомок3
В-дерево
КОРАЛЛ ПЕЧЕНЕГ

ЕЖИК ИСКРА МАРКЕР НОВОСТЬ ТОПЛИВО

ЖАБРЫ ЗАПОЛЯРЬЕ
В-дерево (поиск)

https://habrahabr.ru/post/114154/
В-дерево (добавление)

https://habrahabr.ru/post/114154/ Добавляем 15
В-дерево (удаление)

https://habrahabr.ru/post/114154/ Добавляем 15
Решение
Плохое решение (В-дерево слов).
Скорость поиска:
lg(размер словаря)*длина слова/2
Объем памяти:
Объем словаря+(2*N+1)*размер словаря
Скорость добавления:
Поиск + добавление одного элемента

Такое же, как двоичное дерево.


Удобнее при работе с диском.
Префиксное дерево
Узел хранит элемент данных, общий для всех
потомков, и указатели на потомков, хранящие
последовательность следующих элементов.
Для получения всей последовательности
необходимо пройти по всем дугам, помеченным
соответствующими элементами.
Часть дуг помечены как конечные (в них
последовательность может заканчиваться).
Префиксное дерево
А Б В

А Б В Г

Б В

А Е

Т Р

И С

С Т

А
Префиксное дерево
Хорошее решение (префиксное дерево).
Скорость поиска:
длина слова
Объем памяти:
Зависит от словаря
Скорость добавления:
Поиск части слова + вставка остального
Расстояние Левенштейна
При сравнении строк учитываются
- пропуски
- вставки
- замены

Расстояние Левенштейна равно количеству


подобных изменений, которые надо внести в
одну строку, чтобы получить другую.
Расстояние Левенштейна
Одноклассники
Аднаклассники L=2
Одоклассники L=1
Однокласссники L=1
Расстояние Хемминга
Количество символов, отличающихся в
одинаковых позициях.

Одноклассники
Аднаклассники H=2
Одоклассники H=10
Однокласссники L=5
И теперь вы готовы к тому,
чтобы начать выполнять
лабораторную работу
номер два