Дерево поиска, наивная реализация — различия между версиями
м (англоязычные термины, тире) |
м |
||
Строка 4: | Строка 4: | ||
=== Обход дерева поиска === | === Обход дерева поиска === | ||
Есть три операции обхода узлов дерева, отличающиеся порядком обхода узлов: | Есть три операции обхода узлов дерева, отличающиеся порядком обхода узлов: | ||
− | * inorderTraversal {{---}} обход узлов в отсортированном порядке | + | * <tex>\mathrm{inorderTraversal}</tex> {{---}} обход узлов в отсортированном порядке |
− | * | + | * <tex>\mathrm{preorderTraversal}</tex> {{---}} обход узлов в порядке: вершина, левое поддерево, правое поддерево |
− | * | + | * <tex>\mathrm{postorderTraversal}</tex> {{---}} обход узлов в порядке: левое поддерево, правое поддерево, вершина |
− | ''' | + | inorderTraversal('''Node''' x) |
'''if''' x != null | '''if''' x != null | ||
− | + | inorderTraversal(x.left) | |
− | '''print''' | + | '''print''' x.key |
− | + | inorderTraversal(x.right) | |
Корректность данного алгоритма следует из свойств бинарного дерева поиска. | Корректность данного алгоритма следует из свойств бинарного дерева поиска. | ||
− | ''' | + | preorderTraversal('''Node''' x) |
'''if''' x != null | '''if''' x != null | ||
− | '''print''' | + | '''print''' x.key |
− | + | preorderTraversal(x.left) | |
− | + | preorderTraversal(x.right) | |
− | ''' | + | postfixTraversal('''Node''' x) |
'''if''' x != null | '''if''' x != null | ||
− | + | postorderTraversal(x.left) | |
− | + | postorderTraversal(x.right) | |
− | '''print''' | + | '''print''' x.key |
Данные алгоритмы выполняют обход за время <tex>O(n)</tex>, поскольку процедура вызывается ровно два раза для каждого узла дерева. | Данные алгоритмы выполняют обход за время <tex>O(n)</tex>, поскольку процедура вызывается ровно два раза для каждого узла дерева. | ||
=== Поиск элемента === | === Поиск элемента === | ||
[[Файл:Bst search.png|thumb|318px|Поиск элемента 4]] | [[Файл:Bst search.png|thumb|318px|Поиск элемента 4]] | ||
Для поиска элемента в бинарном дереве поиска можно воспользоваться следующей процедурой, которая принимает в качестве параметров корень дерева и искомый ключ. Для каждого узла функция сравнивает значение его ключа с искомым ключом. Если ключи одинаковы, то функция возвращает текущий узел, в противном случае функция вызывается рекурсивно для левого или правого поддерева. Узлы, которые посещает функция образуют нисходящий путь от корня, так что время ее работы <tex>O(h)</tex>, где <tex>h</tex> {{---}} высота дерева. | Для поиска элемента в бинарном дереве поиска можно воспользоваться следующей процедурой, которая принимает в качестве параметров корень дерева и искомый ключ. Для каждого узла функция сравнивает значение его ключа с искомым ключом. Если ключи одинаковы, то функция возвращает текущий узел, в противном случае функция вызывается рекурсивно для левого или правого поддерева. Узлы, которые посещает функция образуют нисходящий путь от корня, так что время ее работы <tex>O(h)</tex>, где <tex>h</tex> {{---}} высота дерева. | ||
− | Node '''search''' | + | '''Node''' search('''Node''' x, '''key''' k) |
'''if''' x == null '''or''' k == x.key | '''if''' x == null '''or''' k == x.key | ||
'''return''' x | '''return''' x | ||
'''if''' k < x.key | '''if''' k < x.key | ||
− | '''return | + | '''return''' search(x.left, k) |
'''else''' | '''else''' | ||
− | '''return | + | '''return''' search(x.right, k) |
=== Поиск минимума и максимума === | === Поиск минимума и максимума === | ||
− | Чтобы найти минимальный элемент в бинарном дереве поиска, необходимо просто следовать указателям left от корня дерева, пока не встретится значение null. Если у вершины есть левое поддерево, то по свойству бинарного дерева поиска в нем хранятся все элементы с меньшим ключом. Если его нет, значит эта вершина и есть минимальная. Аналогично ищется и максимальный элемент. Для этого нужно следовать правым указателям. | + | Чтобы найти минимальный элемент в бинарном дереве поиска, необходимо просто следовать указателям <tex>left</tex> от корня дерева, пока не встретится значение <tex>null</tex>. Если у вершины есть левое поддерево, то по свойству бинарного дерева поиска в нем хранятся все элементы с меньшим ключом. Если его нет, значит эта вершина и есть минимальная. Аналогично ищется и максимальный элемент. Для этого нужно следовать правым указателям. |
− | Node '''minimum''' | + | '''Node''' minimum('''Node''' x) |
'''if''' x.left == null | '''if''' x.left == null | ||
'''return''' x | '''return''' x | ||
− | '''return | + | '''return''' minimum(x.left) |
− | Node '''maximum''' | + | '''Node''' maximum('''Node''' x) |
'''if''' x.right == null | '''if''' x.right == null | ||
'''return''' x | '''return''' x | ||
− | '''return | + | '''return''' maximum(x.right) |
Данные функции принимают корень поддерева, и возвращают минимальный (максимальный) элемент в поддереве. Обе процедуры выполняются за время <tex>O(h)</tex>. | Данные функции принимают корень поддерева, и возвращают минимальный (максимальный) элемент в поддереве. Обе процедуры выполняются за время <tex>O(h)</tex>. | ||
=== Поиск следующего и предыдущего элемента === | === Поиск следующего и предыдущего элемента === | ||
Если у узла есть правое поддерево, то следующий за ним элемент будет минимальным элементом в этом поддереве. Если у него нет правого поддерева, то нужно следовать вверх, пока не встретим узел, который является левым дочерним узлом своего родителя. Поиск предыдущего выполнятся аналогично. Если у узла есть левое поддерево, то следующий за ним элемент будет максимальным элементом в этом поддереве. Если у него нет левого поддерева, то нужно следовать вверх, пока не встретим узел, который является правым дочерним узлом своего родителя. | Если у узла есть правое поддерево, то следующий за ним элемент будет минимальным элементом в этом поддереве. Если у него нет правого поддерева, то нужно следовать вверх, пока не встретим узел, который является левым дочерним узлом своего родителя. Поиск предыдущего выполнятся аналогично. Если у узла есть левое поддерево, то следующий за ним элемент будет максимальным элементом в этом поддереве. Если у него нет левого поддерева, то нужно следовать вверх, пока не встретим узел, который является правым дочерним узлом своего родителя. | ||
− | Node '''next''' | + | '''Node''' next('''Node''' x) |
'''if''' x.right != null | '''if''' x.right != null | ||
− | '''return | + | '''return''' minimum(x.right) |
y = x.parent | y = x.parent | ||
'''while''' y != null '''and''' x == y.right | '''while''' y != null '''and''' x == y.right | ||
Строка 60: | Строка 60: | ||
'''return''' y | '''return''' y | ||
− | Node '''prev''' | + | '''Node''' prev('''Node''' x) |
'''if''' x.left != null | '''if''' x.left != null | ||
− | '''return | + | '''return''' maximum(x.left) |
y = x.parent | y = x.parent | ||
'''while''' y != null '''and''' x == y.left | '''while''' y != null '''and''' x == y.left | ||
Строка 71: | Строка 71: | ||
=== Вставка === | === Вставка === | ||
Операция вставки работает аналогично поиску элемента, только при обнаружении у элемента отсутствия ребенка нужно подвесить на него вставляемый элемент. | Операция вставки работает аналогично поиску элемента, только при обнаружении у элемента отсутствия ребенка нужно подвесить на него вставляемый элемент. | ||
− | ''' | + | insert('''Node''' x, '''Node''' z) <font color="green">// корень поддерева, вставляемый элемент</font> |
'''if''' z.key > x.key | '''if''' z.key > x.key | ||
'''if''' x.right != null | '''if''' x.right != null | ||
− | + | insert(x.right, z) | |
'''else''' | '''else''' | ||
z.parent = x; | z.parent = x; | ||
Строка 80: | Строка 80: | ||
'''else''' | '''else''' | ||
'''if''' x.left != null | '''if''' x.left != null | ||
− | + | insert(x.left, z) | |
'''else''' | '''else''' | ||
z.parent = x; | z.parent = x; | ||
Строка 101: | Строка 101: | ||
| [[Файл:Bst_del3.png|900px]] | | [[Файл:Bst_del3.png|900px]] | ||
|} | |} | ||
− | ''' | + | delete('''Node''' root, '''Node''' z) <font color="green">// корень поддерева, удаляемый элемент</font> |
− | Node x, y | + | '''Node''' x, y <font color="green">// x - элемент, который нужно поместить вместо y</font> |
− | '''if''' z.left != null '''and''' z.right != null | + | '''if''' z.left != null '''and''' z.right != null <font color="green">// если z имеет двух детей</font> |
− | y = '''next'''(z) | + | y = '''next'''(z) <font color="green">// y - элемент, следующий за удаляемым, x - null</font> |
x = null | x = null | ||
'''if''' y == y.parent.left | '''if''' y == y.parent.left | ||
Строка 110: | Строка 110: | ||
'''else''' | '''else''' | ||
y.parent.right = null | y.parent.right = null | ||
− | z.key = y.key | + | z.key = y.key <font color="green">// подвешиваем y вместо z</font> |
z.data = y.data | z.data = y.data | ||
− | '''else if''' z.left != null '''or''' z.right != null | + | '''else if''' z.left != null '''or''' z.right != null <font color="green">// eсли z имеет одного ребенка</font> |
− | y = z | + | y = z <font color="green">// y - удаляемый элемент</font> |
− | '''if''' y.left != null | + | '''if''' y.left != null <font color="green">// x - ребенок y</font> |
− | x = y.left | + | x = y.left |
'''else''' | '''else''' | ||
x = y.right | x = y.right | ||
− | '''else''' | + | '''else''' <font color="green">// если z не имеет детей</font> |
− | y = z | + | y = z <font color="green">// y - удаляемый элемент, x - null</font> |
x = null | x = null | ||
− | '''if''' x != null | + | '''if''' x != null <font color="green">// подвешиваем x вместо y</font> |
− | x.parent = y.parent | + | x.parent = y.parent |
'''if''' y.parent == null | '''if''' y.parent == null | ||
root = x | root = x |
Версия 13:07, 24 мая 2015
Бинарное дерево поиска обладает следующим свойством: если
— узел бинарного дерева с ключом , то все узлы в левом поддереве должны иметь ключи, меньшие , а в правом поддереве большие .Содержание
Операции в бинарном дереве поиска
Обход дерева поиска
Есть три операции обхода узлов дерева, отличающиеся порядком обхода узлов:
- — обход узлов в отсортированном порядке
- — обход узлов в порядке: вершина, левое поддерево, правое поддерево
- — обход узлов в порядке: левое поддерево, правое поддерево, вершина
inorderTraversal(Node x) if x != null inorderTraversal(x.left) print x.key inorderTraversal(x.right)
Корректность данного алгоритма следует из свойств бинарного дерева поиска.
preorderTraversal(Node x) if x != null print x.key preorderTraversal(x.left) preorderTraversal(x.right)
postfixTraversal(Node x) if x != null postorderTraversal(x.left) postorderTraversal(x.right) print x.key
Данные алгоритмы выполняют обход за время
, поскольку процедура вызывается ровно два раза для каждого узла дерева.Поиск элемента
Для поиска элемента в бинарном дереве поиска можно воспользоваться следующей процедурой, которая принимает в качестве параметров корень дерева и искомый ключ. Для каждого узла функция сравнивает значение его ключа с искомым ключом. Если ключи одинаковы, то функция возвращает текущий узел, в противном случае функция вызывается рекурсивно для левого или правого поддерева. Узлы, которые посещает функция образуют нисходящий путь от корня, так что время ее работы
, где — высота дерева.Node search(Node x, key k) if x == null or k == x.key return x if k < x.key return search(x.left, k) else return search(x.right, k)
Поиск минимума и максимума
Чтобы найти минимальный элемент в бинарном дереве поиска, необходимо просто следовать указателям
от корня дерева, пока не встретится значение . Если у вершины есть левое поддерево, то по свойству бинарного дерева поиска в нем хранятся все элементы с меньшим ключом. Если его нет, значит эта вершина и есть минимальная. Аналогично ищется и максимальный элемент. Для этого нужно следовать правым указателям.Node minimum(Node x) if x.left == null return x return minimum(x.left)
Node maximum(Node x) if x.right == null return x return maximum(x.right)
Данные функции принимают корень поддерева, и возвращают минимальный (максимальный) элемент в поддереве. Обе процедуры выполняются за время
.Поиск следующего и предыдущего элемента
Если у узла есть правое поддерево, то следующий за ним элемент будет минимальным элементом в этом поддереве. Если у него нет правого поддерева, то нужно следовать вверх, пока не встретим узел, который является левым дочерним узлом своего родителя. Поиск предыдущего выполнятся аналогично. Если у узла есть левое поддерево, то следующий за ним элемент будет максимальным элементом в этом поддереве. Если у него нет левого поддерева, то нужно следовать вверх, пока не встретим узел, который является правым дочерним узлом своего родителя.
Node next(Node x) if x.right != null return minimum(x.right) y = x.parent while y != null and x == y.right x = y y = y.parent return y
Node prev(Node x) if x.left != null return maximum(x.left) y = x.parent while y != null and x == y.left x = y y = y.parent return y
Обе операции выполняются за время
.Вставка
Операция вставки работает аналогично поиску элемента, только при обнаружении у элемента отсутствия ребенка нужно подвесить на него вставляемый элемент.
insert(Node x, Node z) // корень поддерева, вставляемый элемент if z.key > x.key if x.right != null insert(x.right, z) else z.parent = x; x.right = z; else if x.left != null insert(x.left, z) else z.parent = x; x.left = z;
Время работы алгоритма
.Удаление
Для удаления узла из бинарного дерева поиска нужно рассмотреть три возможные ситуации. Если у узла нет дочерних узлов, то у его родителя нужно просто заменить указатель на null. Если у узла есть только один дочерний узел, то нужно создать новую связь между родителем удаляемого узла и его дочерним узлом. Наконец, если у узла два дочерних узла, то нужно найти следующий за ним элемент(у этого элемента не будет левого потомка) и переместить его на место удаляемого узла. Время работы алгоритма
.Случай | Иллюстрация |
---|---|
Удаление листа | |
Удаление узла с одним дочерним узлом | |
Удаление узла с двумя дочерними узлами |
delete(Node root, Node z) // корень поддерева, удаляемый элемент Node x, y // x - элемент, который нужно поместить вместо y if z.left != null and z.right != null // если z имеет двух детей y = next(z) // y - элемент, следующий за удаляемым, x - null x = null if y == y.parent.left y.parent.left = null else y.parent.right = null z.key = y.key // подвешиваем y вместо z z.data = y.data else if z.left != null or z.right != null // eсли z имеет одного ребенка y = z // y - удаляемый элемент if y.left != null // x - ребенок y x = y.left else x = y.right else // если z не имеет детей y = z // y - удаляемый элемент, x - null x = null if x != null // подвешиваем x вместо y x.parent = y.parent if y.parent == null root = x else if y == y.parent.left y.parent.left = x else y.parent.right = x
Ссылки
Литература
1. Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн, К. Алгоритмы: построение и анализ = Introduction to Algorithms / Под ред. И. В. Красикова. — 2-е изд. — М.: Вильямс, 2005. — 1296 с. — ISBN 5-8459-0857-4