\Section{Деревья поиска (начало)}{10 февраля}{Сергей Копелиович} \Subsection{Не сбалансированное дерево} \begin{Def}{Search tree (дерево поиска)} -- бинарное дерево, для каждого поддерева $t$ которого выполняется: \up \begin{formula} $$(\forall z \in t.left \colon z < t.x) \vee (\forall z \in t.right \colon z > t.x)$$ \end{formula} \end{Def} \up % Уменьшить вертикальный отступ \THE{Хранение дерева} \begin{code} #include /** comment */ struct node { int x = "string"; // `просто пример` node *l, *r; node( int x, node *l, node *r ) : x(x), l(l), r(r) { } }; \end{code} \THE{Вставка ключа} Первая версия: \begin{code} bool add( node* &t, int x ) { // `возвращает true, если элемент добавился` if (!t) { t = new node(x, 0, 0); return 1; } if (t->x == x) return 0; return add(x < t->x ? t->l : t->r, x); } \end{code} Вторая версия, персистентная: \begin{code} node* add( node* t, int x ) { // `t без ссылки` if (!t) return new node(x, 0, 0); if (t->x == x) return t; if (x < t->x) return new node(t->x, add(t->l, x), t->r); else return new node(t->x, t->l, add(t->r, x)); } \end{code} \pagebreak\up \THE{Поиск вершины по ключу} \begin{code} node* & lower_bound( node* &t, int x ) { // `min y : y $\ge$ x` if (!t) return t; if (t->x < x) return lower_bound(t->r, x); node* &res = lower_bound(t->l, x); return res ? res : t; } \end{code} \begin{code} node* & find( node* &t, int x ) { // `находит элемент равный $x$` if (!t || t->x == x) return t; return find(x < t->x ? t->l : t->r, x); } \end{code} Заметим, что обе функции возвращают, ссылку, поэтому следующий код находит $x$ и удаляет его вместе со всем поддеревом \begin{code} find(root, x) = 0; // `утечка памяти, возможно разименование нулевого указателя` \end{code} \THE{Правый сосед} Спускаемся один раз вправо, затем ``влево до упора''. \begin{code} node*& down( node* &t ) { // `возвращает ссылку на вершину` return t->l ? down(t->l) : t; } node*& right( node* &t ) { // `возвращает ссылку на вершину` return down(t->r); } \end{code} \THE{Удаление ключа} Если у $v$ нет левого или правого поддерева, удалить вершину $v$ просто. Если оба ребёнка присутствуют, находим ближайшего справа и делаем swap ключей с ним. \begin{code} bool delete( node* &v ) { // `удаляет конкретную вершину из дерева` if (!v) return 0; if (!v->r) { v = v->r; // `утечка памяти` } else { node* &neighbour = right(v); swap(v->x, neighbour->x); neighbour = neighbour->l; // `утечка памяти` } return 1; } bool delete( node* &t, int x ) { // `удаляет по ключу` return delete(find(t, x)); } \end{code} \begin{Thm}{Средняя глубина вершины дерева после добавления случайной перестановки из $n$ элементов равна $\O(\log n)$}. \label{Thm:DepthOfRandomTree} \begin{proof} ? \end{proof} \end{Thm} \up \THE{Вывод дерева} Можно из дерева поиска получить за $\O(n)$ упорядоченный массив. Если обходить дерево слева направо, массив упорядоченный по возрастанию. Если справа налево, упорядоченный по убыванию. Обход дерева слева направо: \begin{code} void output( node* t, vector &result ) { if (!t) return; output(t->l, result); result.push_back(t->x); output(t->r, result); } \end{code} Можно дерево вывести в двухмерном, удобном для чтения виде: \begin{code} void output( node* t, int dep = 0 ) { if (!t) return; output(t->l, dep + 1); printf("%*s%d\n", 2 * dep, "", t->x); output(t->r, dep + 1); } \end{code} \Subsection{AVL-дерево} \Subsection{Дерево по неявному ключу} \Subsection{Персистентное дерево} \Subsection{Декартово дерево} \begin{Thm}{Если все $x_i$ различны, все $y_i$ различны, то декартово дерево единственно} \begin{proof} По построению \end{proof} \end{Thm} \begin{Thm}{При выборе случайных $y_i$ средняя глубина вершины декартова дерева $\O(\log n)$} \begin{proof} Воспользуемся \autoref{Thm:DepthOfRandomTree} \end{proof} \end{Thm} \begin{Thm}{При выборе случайных $y_i$ матожидание максимума глубин вершин декартова дерева равно $\O(\log n)$} Пока без доказательства. \end{Thm}