Скачать презентацию Алгоритмы на деревьях Определение дерева 8 10 6

• Количество слайдов: 28

Алгоритмы на деревьях. Определение дерева 8 10 6 3 1 9 4 12 class Tree { static private class Tree. Node { Object key; Tree. Node left, right; public Tree. Node(Object key, Tree. Node left, Tree. Node right) { this. key = key; this. left = left; this. right = right; } public Tree. Node(Object key) { this(key, null); } } Tree. Node root = null; public Tree() {} public Tree(Tree left, Object key, Tree right) { root = new Tree. Node(key, left. root, right. root); } }

Простые рекурсивные алгоритмы class Tree { static private class Tree. Node { Object key; Tree. Node left, right; } Tree. Node root = null; public int height() { return height(root); } private int height(Tree. Node root) { if (root == null) return 0; else return Math. max(height(root. left), height(root. right)) + 1; } } public int level(int n) { return level(root, n); } private int level(Tree. Node root, int n) { return root == null ? 0 : n == 0 ? 1 : level(root. left, n-1) + level(root. right, n-1); }

Внутренний итератор public interface Visitor { void visit(Object node); } class Main { public static void main(String[] args) { Tree t =. . . ; t. iterate(new Visitor() { public void visit(Object node) { System. out. println(node); } }); class Tree { static private class Tree. Node { Object key; Tree. Node left, right; } } Tree. Node root = null; public void iterate(Visitor v) { iterate(root, v); } private void iterate(Tree. Node root, Visitor v) { if (root != null) { iterate(root. left, v); v. visit(root. key); iterate(root. right, v); } }

Внешний итератор 10 8 6 3 8 10 6 3 1 1 9 12 4 3 4 6 8 9 10 12

Внешний итератор class Tree { private static class Tree. Iterator implements Iterator { Stack stk = new Stack(); Tree. Node current; public Tree. Iterator(Tree. Node root) { current = root; if (current != null) find. Left(); } private void find. Left() { while (current. left != null) { stk. push(current); current = current. left; } } public boolean has. Next() { return current != null; } public Object next() { if (current == null) throw new No. Such. Element. Exception(); Tree. Node res = current; current = current. right; if (current != null) find. Left(); else if (!stk. is. Empty()) { current = (Tree. Node)stk. pop(); } return res. key; } public void remove() { throw new Unsupported. Operation. Exception(); } } public Iterator iterator() { return new Tree. Iterator(root); } }

Внешний итератор для обхода дерева «по уровням» class Tree { private static class Tree. Iterator 1 implements Iterator { Queue que = new Queue(); public Tree. Iterator 1(Tree. Node root) { if (root != null) que. enqueue(root); } public boolean has. Next() { return !que. is. Empty(); } public Object next() { if (que. is. Empty()) throw new No. Such. Element. Exception(); Tree. Node res = (Tree. Node)que. dequeue(); if (res. left != null) que. enqueue(res. left); if (res. right != null) que. enqueue(res. right); return res. ref; } public void remove() { throw new Unsupported. Operation. Exception(); } } public Iterator iterator() { return new Tree. Iterator(root); } }

Деревья поиска. Индексация и поиск данных. interface Comparable { int compare. To(Object obj); } Ищем ключ 9 8 Поиск в дереве по ключу class Tree { static private class Tree. Node { Comparable key; Object ref; Tree. Node left, right; } Tree. Node root = null; 3 1 public Object search(Comparable key) { for (Tree. Node current = root; current != null; ) { int res = key. compare. To(current. key); if (res < 0) { current = current. left; } else if (res == 0) { return current. ref; } else { // res > 0 current = current. right; } } return null; } 10 6 9 12 4 }

Индексация данных на; 8 берегу; 6 пустынных; 1 10 6 12 9 12 4 стоял; 3 волн; 9 он; 10 дум; великих; 1 3 8 4 С помощью поиска по индексу можно получить ответы на вопросы: Ø Какое слово встречается ровно 6 раз? Ø Какие слова встречаются больше 10 раз? Ø Какое слово встречается чаще всего?

Добавление данных в лист дерева public void add. Leaf(Comparable key, Object obj) { Tree. Node new. Node = new Tree. Node(key, obj); Tree. Node current = root; Tree. Node pred = null; 8 10 6 3 1 7 4 9 while (current != null) { int res = key. compare. To(current. key); if (res == 0) return; else { pred = current; current = (res < 0 ? current. left : current. right); } } if (pred == null) root = new. Node; else { if (key. compare. To(pred. key) < 0) pred. left = new. Node; else pred. right = new. Node; } 12 11 }

Добавление данных в корень дерева public void add. Root(Comparable key, Object obj) { Tree. Node new. Node = new Tree. Node(key, obj); add. Root(key, root, new. Node, 0, new. Node, 1); root = new. Node; } 7 8 10 6 3 1 9 4 12 private void add. Root(Comparable key, Tree. Node current, Tree. Node left. Ref, int lr. Left, Tree. Node right. Ref, int lr. Right) { if (current == null) { if (lr. Left == 0) left. Ref. left = null; else left. Ref. right = null; if (lr. Right == 0) right. Ref. left = null; else right. Ref. right = null; } else if (key. compare. To(current. key) > 0) { if (lr. Left == 0) left. Ref. left = current; else left. Ref. right = current; add. Root(key, current. right, current, 1, right. Ref, lr. Right); } else { if (lr. Right == 0) right. Ref. left = current; else right. Ref. right = current; add. Root(key, current. left, left. Ref, lr. Left, current, 0); } }

Сбалансированные по высоте (АВЛ) деревья 1 1 10 8 1 2 1 0 0 3 1 0 9 0 4 0 7 10 6 0 0 2 1 0 13 0 0 1 0 0 4 12 9 -1 0 12 0 1 0 0 6 0 3 В общем случае трудно придумать алгоритм, позволяющий сбалансировать произвольное дерево, но можно предложить алгоритм балансировки дерева после добавления или удаления одного узла. 15

Алгоритм «простого поворота» 1. Отсоединение поддеревьев 2. Поворот 3. Присоединение поддеревьев 2 0 3 0 1 2 1 (h) I IV (h) 1 III (h) II (h-1) (I) < (1) < (II) < (2) < (III) < (3) < (IV)

Алгоритм «двойного поворота» 1. Отсоединение поддеревьев 2. Поворот 3. Присоединение поддеревьев 2 0 3 0 -1 -1 1 (h) IV (h) 1 I 2 (h) II III (h-1) (I) < (1) < (II) < (2) < (III) < (3) < (IV)

Пример вставки ключа 10 0 0 10 1 6 1 0 8 1 0 18 0 17 -1 0 23 18 0 3 20 0 1 1 -1 1 0 12 0 6 15 0 3 1 -2 -1 8 15 20 0 0 1 -1 0 0 12 17 0 23

Красно-черные деревья 17 Свойства красно-черных деревьев 14 21 10 7 3 16 12 Ø Корень и пустые узлы всегда имеют черный цвет Ø Красная вершина не может иметь красных потомков 15 19 23 Ø Максимальные «черные» длины путей, ведущих из корня к листьям (пустым узлам), одинаковы 20 Красно-черные деревья поиска достаточно эффективны: число шагов при поиске не превышает 2 log 2 n Существуют эффективные процедуры добавления и удаления узлов, которые не нарушают свойств красно-черных деревьев

Алгоритм добавления ключа в красно-черное дерево 17 1. Добавляем ключ 19 2. Добавляем ключ 20 10 26 3. Добавляем ключ 11 4. Добавляем ключ 3… 6 4 1 15 7 13 11 3 21 19 29 24 20

Хранение дополнительной информации в деревьях. Задача: по заданному порядковому номеру найти узел дерева; и наоборот, по заданному узлу найти его порядковый номер. Массив: 0 3 1 7 2 10 3 12 4 14 5 15 6 16 7 17 8 19 9 20 10 21 11 17 12 Решение: в каждом узле хранить размер поддерева с корнем в этом узле 23 14 7 21 4 10 4 7 2 3 1 16 2 12 1 15 1 19 2 23 1 20 1

Алгоритмы работы с индексами в двоичных деревьях с размерами Поиск позиции по индексу: 17 12 Tree. Node find. By. Index(int i) 1. Начинаем цикл поиска с корня current = root; 2. 14 7 Если в левом поддереве (== i) узлов, return current; 3. Если в левом поддереве (> i) узлов, current = current. left; 4. 7 2 i == 5 Выход: 21 4 10 4 Если в левом поддереве (< i) узлов, i -= (current. left. size + 1); current = current. right; Вход: i = 5 key == 15 3 1 16 2 12 1 15 1 i =19 0 23 1 2 20 1

Программа поиска позиции узла в дереве по индексу class Tree { private Tree. Node root = null; public static class Tree. Node { Comparable info; int size; Tree. Node parent, left, right; } private Tree. Node find. By. Index(int i) { // Pre: root != null && // 0 <= i < root. size Tree. Node current = root; for (; ; ) { // Inv: 0 <= i < current. size int size. L = 0; if (current. left != null) size. L = current. left. size; if (i == size. L) return current; else if (i < size. L) current = current. left; else { i -= size. L+1; current = current. right; } } }

Алгоритмы работы с индексами в двоичных деревьях с размерами Поиск индекса по позиции: 17 12 Tree. Node find. By. Position(Tree. Node node) 1. Начинаем цикл поиска с узла node; ndx = node. left. size; 2. Если узел справа от родительского, ndx += (brother. size+1); 3. 14 7 Переходим к родительскому узлу, node = node. parent; Вход: ndx == 6 16 2 ndx = 1 6 19 2 23 1 key == 16 Выход: 10 4 21 4 7 2 3 1 12 1 15 1 20 1

Программа поиска индекса узла в дереве по позиции class Tree { private Tree. Node root = null; public static class Tree. Node { Comparable info; int size; Tree. Node parent, left, right; } private int find. By. Position(Tree. Node node) { int ndx = 0; if (node. left != null) ndx += node. left. size; for (Tree. Node p = node. parent; p != null; p = p. parent) { if (p. right == node) { int size. L = 0; if (p. left != null) size. L = p. left. size; ndx += size. L+1; } node = p; } return ndx; }

Преобразование информации о размерах при вращениях B s. B A s 1 A s 3 s 2 B s 1 s 2 s. A == s 1 + s 2 + 1 s. A == s 1 + s. B + 1 s. B == s. A + s 3 + 1 s. B == s 2 + s 3 + 1 Алгоритм: int prev_s. A = s. A; s. A = s. B; s. B += s 2 – prev_s. A; s 3

2 -3 -дерево 18 32 7 13 1 4 9 12 24 16 21 26 29 37 41 34 39 44 46 Структура 2 -3 -дерева: Ø Каждый узел содержит 1 или 2 ключа Ø Ключи упорядочены (возможен быстрый поиск) Ø Промежуточные узлы имеют все ссылки (2 или 3 ссылки) Ø Все терминальные узлы (листья) находятся на одном уровне

Алгоритмы вставки ключа в 2 -3 -дерево 0 18 32 7 13 1 4 9 12 48 33 24 16 21 26 29 37 41 34 39 44 46 Новый ключ вставляется в лист одним из трех методов: Ø Расширением терминального узла Ø «Переливанием» Ø Расщеплением узлов

Алгоритмы удаления ключа из 2 -3 -дерева 18 32 7 13 1 4 9 12 24 16 21 26 29 37 41 34 39 44 46 Существующий ключ переносится в лист и удаляется одним из трех методов: Ø Сужением терминального узла Ø «Переливанием» Ø Склеиванием узлов

Обобщение 2 -3 -дерева – В-дерево k-го порядка Пример структуры при k = 3 Структура В-дерева: Ø Корневой узел содержит от 1 до 2*k ключей Ø Прочие узлы содержат от k до 2*k ключей Ø Ключи упорядочены (возможен быстрый поиск) Ø Промежуточные узлы имеют все ссылки (корень – от 2, остальные – от (k+1) до (2*k+1) ссылки) Ø Все терминальные узлы (листья) находятся на одном уровне

Расщепление узла В-дерева k-го порядка при вставке ключа k кл. * k +11) ключ (2 k кл. 1. При вставке ключа в терминальный узел образовалось переполнение узла 2. Делим узел на 3 узла: k, 1 и k ключей 3. Перемещаем средний ключ на предыдущий уровень

Модифицированное В-дерево (В+-дерево) 1. Только терминальные узлы содержат ссылки на данные; промежуточные узлы служат только для поиска информации. 2. При расщеплении узла происходит создание копии ключа: 1 k + 2*k + 1 k 1 3. При слиянии узлов ключ, пришедший с более высокого уровня, уничтожается: 1 k-1 - 1 2*k k