Скачать презентацию Деревья теория и практика использования Шаблон класса Скачать презентацию Деревья теория и практика использования Шаблон класса

Trees-Intro.pptx

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

Деревья: теория и практика использования Деревья: теория и практика использования

Шаблон класса бинарного дерева template <class T> class Tree { struct Node { T Шаблон класса бинарного дерева template class Tree { struct Node { T item; Node *left; Node *right; Node( const T& item, Node *left = 0, Node *right = 0 ) { Node: : item = item; Node: : left = left; Node: : right = right; }; public: Node *root; Tree() { root = 0; } ~Tree(); };

Дерево общего вида как бинарное template <class T> class Tree { public: struct Node Дерево общего вида как бинарное template class Tree { public: struct Node { T item; Node *son; Node *brother; Node( const T& item, Node *son = 0, Node *brother = 0 ) { Node: : item = item; Node: : son = son; Node: : brother = brother; }; private: Node *root; public: Tree() { root = 0; } ~Tree(); };

Высота бинарного дерева Высота бинарного дерева

Формальное определение высоты дерева Упражнение Реализуйте рекурсивный алгоритм определения высоты дерева Формальное определение высоты дерева Упражнение Реализуйте рекурсивный алгоритм определения высоты дерева

Определение высоты дерева template <class T> int Tree<T>: : height() const { if( root Определение высоты дерева template int Tree: : height() const { if( root == 0 ) return 0; return root->height(); } template int Tree: : Node: : height() const { int max = 0; for( Node *cur = son; cur != 0; cur = cur->brother ) { int cur. Height = cur->height(); if( cur. Height > max ) max = cur. Height; } return max+1; };

Важные для практики частные случаи бинарных деревьев Во многих случаях информация о взаимном расположении Важные для практики частные случаи бинарных деревьев Во многих случаях информация о взаимном расположении узлов может определяться способом размещения древовидной структуры в памяти Удается обойтись без явных указателей на дочерние и родительские узлы «Упакованное» бинарное дерево

Полностью сбалансированное дерево Полностью сбалансированное дерево

Хранение сбалансированного дерева в виде одномерного массива Пусть r – номер узла Тогда номер Хранение сбалансированного дерева в виде одномерного массива Пусть r – номер узла Тогда номер родителя: r/2 Номер корня левого поддерева: 2 r Номер корня правого поддерева: 2 r+1 Индекс элемента в массиве: r-1 Дерево можно «упаковать» в одномерный массив и операции над узлами свести к операциям над индексами элементов массива

Просмотр от самого левого узла к самому правому // Обработать бинарное дерево из n Просмотр от самого левого узла к самому правому // Обработать бинарное дерево из n узлов с // корнем в узле no. Root void Process. Tree( int no. Root ) { if( no. Root > n ) return; // выход из рекурсии // Обработать левое поддерево Process. Tree( no. Root*2 ); // Обработать корень дерева //. . . // Обработать правое поддерево Process. Tree( no. Root*2 + 1 ); }

Сортировка с использованием бинарного дерева (heap sort) Другие названия: «сортировка сложным выбором» , «пирамидальная Сортировка с использованием бинарного дерева (heap sort) Другие названия: «сортировка сложным выбором» , «пирамидальная сортировка» Алгоритм также использует обработку «упакованного» бинарного дерева В основе – понятие пирамиды: ◦ Для любого узла r, имеющего одного потомка, справедливо: ◦ Для любого узла r, имеющего двух потомков, ◦ справедливо:

Пирамидальная сортировка: свойства пирамиды Таким образом, корневая вершина поддерева, являющегося пирамидой, содержит, соответствует наибольшему Пирамидальная сортировка: свойства пирамиды Таким образом, корневая вершина поддерева, являющегося пирамидой, содержит, соответствует наибольшему значению ключа в этом поддереве Любой лист дерева является пирамидой по определению

Пирамидальная сортировка: описание процесса сортировки for( last = n; // Номер последнего узла // Пирамидальная сортировка: описание процесса сортировки for( last = n; // Номер последнего узла // в начале равен n last > 0; // Продолжаем, пока не рассмотрели // все узлы last-- ) { // Поменять местами узлы с номерами // 1 (корневой) и last //. . . // Восстановить пирамиду из дерева с узлами // от 1 до last-1 (просеивание) //. . . }

Пирамидальная сортировка: восстановление пирамиды (1) Пирамидальная сортировка: восстановление пирамиды (1)

Пирамидальная сортировка: восстановление пирамиды (2) Пирамидальная сортировка: восстановление пирамиды (2)

Пирамидальная сортировка: восстановление пирамиды (3) Пирамидальная сортировка: восстановление пирамиды (3)

Пирамидальная сортировка: восстановление пирамиды (4) Пирамидальная сортировка: восстановление пирамиды (4)

Пирамидальная сортировка: функция просеивания «дыр» template <class T> void Sift( T *a, int root, Пирамидальная сортировка: функция просеивания «дыр» template void Sift( T *a, int root, int last ) { int hole = root; T x = a[ hole-1 ]; for( ; ; ) { int left = 2*hole; int right = left + 1; int pretender; // Номер претендента if( left > last ) break; // Если лист – // просеивание // закончено

Пирамидальная сортировка: функция просеивания «дыр» // Теперь нужно выбрать // претендента на заполнение «дыры» Пирамидальная сортировка: функция просеивания «дыр» // Теперь нужно выбрать // претендента на заполнение «дыры» if( left == last ) pretender = left; else { if( a[ left-1 ] < a[ right-1 ] ) pretender = right; else pretender = left; }

Пирамидальная сортировка: функция просеивания «дыр» // Если претендент не лучше x – // просеивание Пирамидальная сортировка: функция просеивания «дыр» // Если претендент не лучше x – // просеивание закончено if( a[ pretender-1 ] < x ) break; if( a[ pretender-1 ] == x ) break; // Иначе: претендент заполняет «дыру» , // при этом образуется новая «дыра» a[ hole-1 ] = a[ pretender-1 ]; hole = pretender; } // Рано или поздно x «возвращается» // в дерево a[ hole-1 ] = x; } // конец функции Sift

Что нужно для сортировки всей последовательности? Подготовить пирамиду в самом начале (это достигается за Что нужно для сортировки всей последовательности? Подготовить пирамиду в самом начале (это достигается за n/2 просеиваний) Осуществить цикл из n-1 просеиваний, на каждой итерации которого элемент с наибольшим значением ключа занимает свое окончательное место

Иллюстрация первоначальной подготовки пирамиды (1) Иллюстрация первоначальной подготовки пирамиды (1)

Иллюстрация первоначальной подготовки пирамиды (2) Иллюстрация первоначальной подготовки пирамиды (2)

Иллюстрация первоначальной подготовки пирамиды (3) Иллюстрация первоначальной подготовки пирамиды (3)

Иллюстрация первоначальной подготовки пирамиды (4) Иллюстрация первоначальной подготовки пирамиды (4)

Иллюстрация первоначальной подготовки пирамиды (5) Иллюстрация первоначальной подготовки пирамиды (5)

Иллюстрация первоначальной подготовки пирамиды (6) Иллюстрация первоначальной подготовки пирамиды (6)

Иллюстрация первоначальной подготовки пирамиды (7) Иллюстрация первоначальной подготовки пирамиды (7)

Иллюстрация первоначальной подготовки пирамиды (8) Иллюстрация первоначальной подготовки пирамиды (8)

Пирамидальная сортировка: реализация template <class T> void Heap. Sort( T *a, int n ) Пирамидальная сортировка: реализация template void Heap. Sort( T *a, int n ) { int root, last; // Первоначальная подготовка пирамиды for( root = n/2; root > 0; root-- ) Sift( a, root, n ); // Цикл сортировки for( last = n; last > 1; last-- ) { T copy = a[ 0 ]; a[ 0 ] = a[ last-1 ]; a[ last-1 ] = copy; Sift( a, 1, last-1 ); } }

Обход дерева Левосторонний инфиксный обход Нисходящий обход в глубину Нисходящий обход в ширину Восходящий Обход дерева Левосторонний инфиксный обход Нисходящий обход в глубину Нисходящий обход в ширину Восходящий обход от корней к вершине ◦ внутренний итератор ◦ рекурсивная процедура ◦ внутренний итератор ◦ стек, хранящий информацию о непройденных поддеревьях ◦ внешний итератор ◦ очередь ◦ внешний итератор ◦ стек

Для простоты используем: template <class T> class Tree { struct Node { T item; Для простоты используем: template class Tree { struct Node { T item; Node *left; Node *right; Node( const T& item, Node *left = 0, Node *right = 0 ) { Node: : item = item; Node: : left = left; Node: : right = right; }; public: Node *root; Tree() { root = 0; } ~Tree(); };

// Левосторонний инфиксный обход template <class T> class Actor { public: virtual void action( // Левосторонний инфиксный обход template class Actor { public: virtual void action( T& node) = 0; }; template void Tree: : traverse. Infix. Left(Actor& a) { recursive. Infix. Left( root, a ); } template void Tree: : recursive. Infix. Left( Node *node, Actor& a ) { if( node ) { recursive. Infix. Left( node->left, a ); a. action( node->item ); recursive. Infix. Left( node->right, a ); } }

// Обход в глубину с использованием стека template <class T> void Tree<T>: : traverse. // Обход в глубину с использованием стека template void Tree: : traverse. Up. Down( Actor& a ) { stack st; Node *cur = root; for( ; ; ) { a. action( cur->item ); if( cur->right != 0 && cur->left != 0 ) { st. push( cur->right ); cur = cur->left; } else if( cur->left != 0 ) { cur = cur->left; } else if( cur->right != 0 ) { cur = cur->right; } else { if( st. empty() ) break; cur = st. top(); st. pop(); } } }

// Преобразование traverse. Infix. Left с // использованием стека template <class T> void Tree<T>: // Преобразование traverse. Infix. Left с // использованием стека template void Tree: : traverse. Infix. Stack( Actor& a ) { stack st; Node *cur = root; while( !st. empty() ) { st. push( cur ); if( cur->left != 0 ) { cur = cur->left; } else do { if( st. empty() ) break; cur = st. top(); st. pop(); a. action( cur->item ); cur = cur->right; } while( cur == 0 ); } }

Коды Хаффмана Сообщения состоят из символов Вероятность появления символов – различная Как закодировать символы, Коды Хаффмана Сообщения состоят из символов Вероятность появления символов – различная Как закодировать символы, чтобы уменьшить длину сообщения? «Регулярный» код: просто, но не оптимально Идея – использовать код переменной длины Префиксное свойство

Пример (Ахо, Хопкрофт, Ульман) Символ Вероятность «Регулярны й» код (1) Код переменной длины (2) Пример (Ахо, Хопкрофт, Ульман) Символ Вероятность «Регулярны й» код (1) Код переменной длины (2) A 0. 12 000 B 0. 33 001 11 C 0. 15 010 01 D 0. 08 011 001 E 0. 32 100 10 Код 1: 001010011 => BCD Код 2: 1101001 => BCD Задача: минимизировать код

Алгоритм Хаффмана Определение структуры дерева, представляющего используемый код Построение леса из деревьев путем объединения Алгоритм Хаффмана Определение структуры дерева, представляющего используемый код Построение леса из деревьев путем объединения листьев, соответствующих кодам с наименьшими вероятностями

C D A B E 0. 15 0. 08 0. 12 0. 33 0. C D A B E 0. 15 0. 08 0. 12 0. 33 0. 32 C D A B E

C D A B E 0. 15 0. 08 0. 12 0. 33 0. C D A B E 0. 15 0. 08 0. 12 0. 33 0. 32 0. 20 D A

C D A B E 0. 15 0. 08 0. 12 0. 33 0. C D A B E 0. 15 0. 08 0. 12 0. 33 0. 32 0. 35 C D A

C D A B E 0. 15 0. 08 0. 12 0. 33 0. C D A B E 0. 15 0. 08 0. 12 0. 33 0. 32 0. 65 0. 35 B C D A E

C D A B E 0. 15 0. 08 0. 12 0. 33 0. C D A B E 0. 15 0. 08 0. 12 0. 33 0. 32 1. 00 B C D A E

C D A B E 00 011 10 11 1 0 C 1 0 C D A B E 00 011 10 11 1 0 C 1 0 1 D A 1 B 0 0 E