Download - Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Transcript
Page 1: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Лекция 7Бинарные деревья поиска

Курносов Михаил Георгиевич

E-mail: [email protected]: www.mkurnosov.net

Курс «Структуры и алгоритмы обработки данных»Сибирский государственный университет телекоммуникаций и информатики (Новосибирск)Весенний семестр, 2016

Page 2: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

АТД «Словарь» (dictionary)

2

Словарь (dictionary) – структура данных для хранения пар вида «ключ» – «значение» (key – value)

Альтернативные название – ассоциативный массив (associative array, map)

В словаре может быть только одна пара с заданным ключом

Ключ (key) Значение (value)

890 Слон

1200 Кит

260 Лев

530 Жираф

Page 3: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

АТД «Словарь» (dictionary)

3

Операция Описание

Add(map, key, value)Добавляет в словарь mapпару (key, value)

Lookup(map, key)Возвращает из словаря mapзначение ассоциированное с ключом key

Remove(map, key) Удаляет из словаря map пару с ключом key

Min(map)Возвращает из словаря mapминимальное значение

Max(map)Возвращает из словаря mapмаксимальное значение

Page 4: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Реализация АТД «Словарь»

4

Реализации словарей отличаются вычислительной сложностью операций и объемом требуемой памяти для хранения пар «ключ-значение»

Распространение получили следующие реализации:

1. Деревья поиска (Search trees)

2. Хэш-таблицы (Hash tables)

3. Списки с пропусками (Skip lists)

4. Связные списки, массивы

Page 5: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Реализация словаря на основе массива

5

ОперацияНеотсортированный

массивОтсортированный

массив

Add(map, key, value)

O(1)(добавление в конец)

O(n)(поиск позиции)

Lookup(map, key)

O(n)O(logn)

(бинарный поиск)

Remove(map, key)

O(n)(поиск элемента и

перенос последнего на место удаляемого)

O(n)(перемещение

элементов)

Min(map) O(n)O(1)

(элемент v[1])

Max(map) O(n)O(1)

(элемент v[n])

Page 6: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Реализация словаря на основе связного списка

6

ОперацияНеотсортированный

связный списокОтсортированный

связный список

Add(map, key, value)

O(1)(добавление

в начало)

O(n)(поиск позиции)

Lookup(map, key)

O(n) O(n)

Remove(map, key)

O(n)(поиск элемента)

O(n)(поиск элемента)

Min(map) O(n) O(1)

Max(map) O(n)O(n) или O(1), если

поддерживать указатель на последний элемент

Page 7: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Бинарные деревья (binary trees)

7

Бинарное дерево (binary tree) – это дерево (структура данных), в котором каждый узел (node) имеет не более двух дочерних узлов (child nodes)

Root node

Leaf node

Child node

Parent node

Depth 0

Depth 1

Depth 2

Depth 3

Page 8: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Бинарные деревья поиска (binary search trees)

8

Двоичное дерево поиска (binary search tree, BST) –это двоичное дерево, в котором:

1) каждый узел x (node) имеет не более двух дочерних узлов (child nodes) и содержит ключ (key) и значение (value)

2) ключи всех узлов левого поддерева узла x меньше значения его ключа

3) ключи всех узлов правого поддерева узла x больше значения его ключа

Key: 530

Value: Жираф

Left Right

Key: 260

Value: Лев

Left Right

Key: 890

Value: Слон

Left Right

Page 9: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Двоичные деревья поиска (binary search trees)

9

Key: 530

Value: Жираф

Left Right

Key: 260

Value: Лев

Left Right

Key: 890

Value: Слон

Left Right

Key: 1200

Value: Кит

Left Right

Словарь Двоичное дерево поиска

Ключ(Key)

Значение (Value)

530 Жираф

260 Лев

890 Слон

1200 Кит

Page 10: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Двоичные деревья поиска (Binary search trees)

10

60Волк

35Рысь

90Ягуар

8Лиса

15Барсук

180Тигр

200Лев

4000Слон

600Медведь

- 9 элементов- глубина (depth) 3

- высота (height) 3

Упорядоченный словарь (ordered map) – словарь, обеспечивающий перебор элементов в упорядоченной последовательности

Операции Prev(key), Next(key)

Min Max

Page 11: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Двоичные деревья поиска (Binary search trees)

11

60Волк

35Рысь

90Ягуар

8Лиса

15Барсук

180Тигр

200Лев

4000Слон

600Медведь

- 9 элементов- глубина (depth) 3

- высота (height) 3

Упорядоченный словарь (ordered map) – словарь, обеспечивающий перебор элементов в упорядоченной последовательности

Операции Prev(key), Next(key)

Min Max// Проход по возрастанию ключейnode = Min(Map)max = Max(Map)while node != max do

// Обработка узла node ...node = Next(node)

end while

Page 12: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Двоичные деревья поиска (Binary search trees)

12

#include <stdio.h>

#include <stdlib.h>

struct bstree {

int key; /* Ключ */

char *value; /* Данные */

struct bstree *left;

struct bstree *right;

};

Page 13: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Создание элемента BST

13

struct bstree *bstree_create(int key,char *value)

{struct bstree *node;

node = malloc(sizeof(*node));if (node != NULL) {

node->key = key;node->value = value;node->left = NULL;node->right = NULL;

}return node;

} TCreate = O(1)

Page 14: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Создание элемента BST

14

int main()

{

struct bstree *tree;

tree = bstree_create(180, "Tigr");

return 0;

}

Page 15: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Добавление элемента в BST

15

60Волк

15Барсук

180Тигр

200Лев

1. Добавление элемента (180, Тигр)

2. Добавление элемента (200, Лев)

3. Добавление элемента (15, Барсук)

4. Добавление элемента (60, Волк)

Ищем листовой узел (leaf node) для вставки

нового элемента

Page 16: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Добавление элемента в BST

void bstree_add(struct bstree *tree, int key, char *value)

{struct bstree *parent, *node;

if (tree == NULL)return;

/* Отыскиваем листовой узел */for (parent = tree; tree != NULL; ) {

parent = tree;if (key < tree->key)

tree = tree->left; else if (key > tree->key)

tree = tree->right;else

return;} 16

60Волк

15Барсук

180Тигр

200Лев

key = 210

parent

Page 17: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Добавление элемента в BST (продолжение)

17

/* Создаем элемент и связываем с узлом */

node = bstree_create(key, value);

if (key < parent->key)

parent->left = node;

else

parent->right = node;

}TAdd = O(h)

При добавлении элемента необходимо спуститься от корня дерева до листа – это требует количества операций порядка высоты h дерева

Поиск листа – O(h), создание элемента и корректировка указателей – O(1)

Page 18: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Поиск элемента в BST

18

60Волк

15Барсук

180Тигр

200Лев

1. Сравниваем ключ корневого узла с искомым. Если совпали, то элемент найден

2. Переходим к левому или правому дочернему узлу и повторяем шаг 1

Возможны рекурсивная и не рекурсивная реализации

Page 19: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Поиск элемента в BST

19

struct bstree *bstree_lookup(struct bstree *tree,

int key)

{

while (tree != NULL) {

if (key == tree->key) {

return tree;

} else if (key < tree->key) {

tree = tree->left;

} else {

tree = tree->right;

}

}

return tree;

} TLookup = O(h)

60Волк

15Барсук

180Тигр

200Лев

key = 60

tree

Page 20: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Поиск минимального элемента в BST

20

60Волк

15Барсук

180Тигр

200Лев

Минимальный элемент всегда расположен в левом поддереве корневого узла

Требуется найти самого левого потомка корневого узла

Page 21: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Поиск минимального элемента в BST

21

struct bstree *bstree_min(struct bstree *tree)

{

if (tree == NULL)

return NULL;

while (tree->left != NULL)

tree = tree->left;

return tree;

} TMin = O(h)

Page 22: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Поиск максимального элемента в BST

22

60Волк

15Барсук

180Тигр

200Лев

Максимальный элемент всегда расположен в правом поддереве корневого узла

Требуется найти самого правого потомка корневого узла

Page 23: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Поиск максимального элемента в BST

23

struct bstree *bstree_max(struct bstree *tree)

{

if (tree == NULL)

return NULL;

while (tree->right != NULL)

tree = tree->right;

return tree;

}

TMax = O(h)

60Волк

15Барсук

180Тигр

200Лев

Page 24: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Пример

24

int main()

{

struct bstree *tree, *node;

tree = bstree_create(180, "Tigr");

bstree_add(tree, 200, "Lev");

bstree_add(tree, 60, "Volk");

node = bstree_lookup(tree, 200);

printf(“Value = %s\n", node->value);

node = bstree_min(tree);

printf("Min: value = %s\n", node->value);

return 0;

}

Page 25: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Удаление элемента из BST

25

10Заяц

15Барсук

180Тигр

200Лев

1. Находим узел z с заданным ключом – O(n)

2. Возможны 3 ситуации:

o узел z не имеет дочерних узлов

o узел z имеет 1 дочерний узел

o узел z имеет 2 дочерних узла60

Волк

90Кабан

Page 26: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Удаление элемента из BST

26

10Заяц

15Барсук

180Тигр

200Лев

Удаление узла “Лев” (случай 1)

1. Находим и удаляем узел “Лев”

из памяти (free)

2. Родительский указатель

(left или right) устанавливаем

в значение NULL

“Тигр”->right = NULL

60Волк

90Кабан

Page 27: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Удаление элемента из BST

27

10Заяц

15Барсук

180Тигр

200Лев

Удаление узла “Волк” (случай 2)

1. Находим узел “Волк”

2. Родительский указатель узла “Волк” (left или right)устанавливаем на его дочерний элемент

3. Удаляем узел “Волк” из памяти

60Волк

90Кабан

“Барсук”->right = “Волк”->right

Page 28: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Удаление элемента из BST

28

10Заяц

15Барсук

180Тигр

200Лев

Удаление узла “Барсук” (случай 3)

1. Находим узел “Барсук”

2. Находим узел с минимальным ключом в правом поддереве узла “Барсук” – самый левый лист в поддереве(узел “Рысь”)

3. Заменяем узел “Барсук”узлом “Рысь”

60Волк

90Кабан

70Тапир

150Марал

45Рысь

55Кабарга

Page 29: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Удаление элемента из BST

29

10Заяц

15Барсук

180Тигр

200Лев

Удаление узла “Барсук” (случай 3)

60Волк

90Кабан

70Тапир

150Марал

45Рысь

55Кабарга

10Заяц

180Тигр

200Лев

60Волк

90Кабан

70Тапир

150Марал

45Рысь

55Кабарга

Page 30: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Анализ эффективность BST

30

1Value

2Value

1. Операции имеют трудоемкость пропорциональную высоте h дерева

2. В худшем случае высота дерева O(n)(вставка элементов в отсортированной последовательности)

3. В среднем случае высота дерева O(logn)3

Value

4Value

NULL

NULL

NULL

bstree_add(tree, “Item”, 1);bstree_add(tree, “Item”, 2);bstree_add(tree, “Item”, 3);bstree_add(tree, “Item”, 4);

Дерево вырождается в связный список

Page 31: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Реализация словаря на основе BST

31

ОперацияСредний случай

(average case)Худший случай

(worst case)

Add(map, key, value)

O(logn) O(n)

Lookup(map, key)

O(logn) O(n)

Remove(map, key)

O(logn) O(n)

Min(map) O(logn) O(n)

Max(map) O(logn) O(n)

Page 32: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Сбалансированные деревья поиска

32

Сбалансированное по высоте дерево поиска (self-balancing binary search tree) – дерево поиска, в котором высоты поддеревьев узла различаются не более чем на заданную константу k

Баланс высоты поддерживается при выполнении операций добавления и удаления элементов

Типы сбалансированных деревьев поиска:

Красно-черные деревья (Red-black tree): ℎ ≤ 2 log2(𝑛 + 1)

АВЛ-деревья (AVL-tree): ℎ < 1.4405 ∙ log2(𝑛 + 2) − 0.3277

B-деревья

Деревья Ван Эмде Боаса

Все операции на красно-черном дереве и АВЛ-дереве в худшем случае выполняются за время O(logn)

Page 33: Лекция 7 - mkurnosov.net · Списки с пропусками ~Skip lists) 4. Связные списки, массивы ... Двоичные деревья поиска

Домашнее чтение

33

[DSABook, Глава 9]

Прочитать в «практике программирования»[KR, С. 67] «2.8 Деревья»

Прочитать в [CLRS, С. 328] раздел об удалении узла из бинарного дерева поиска (функции Tree-Delete, Transplant)

Прочитать про обходы дерева в глубину (pre-order, in-order и post-order)

Как освободить память из под всего дерева зная указатель на корень?