Редактирование: Rope
Внимание! Вы не авторизовались на сайте. Ваш IP-адрес будет публично видимым, если вы будете вносить любые правки. Если вы войдёте или создадите учётную запись, правки вместо этого будут связаны с вашим именем пользователя, а также у вас появятся другие преимущества.
Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.
Текущая версия | Ваш текст | ||
Строка 1: | Строка 1: | ||
'''Rope''' (рус. ''веревка'') {{---}} структура данных для хранения строки, представляющая из себя двоичное сбалансированное дерево и позволяющая делать операции вставки, удаления и конкатенации с логарифмической асимптотикой. | '''Rope''' (рус. ''веревка'') {{---}} структура данных для хранения строки, представляющая из себя двоичное сбалансированное дерево и позволяющая делать операции вставки, удаления и конкатенации с логарифмической асимптотикой. | ||
− | + | Заведем [[Дерево поиска, наивная реализация|двоичное дерево поиска]]. В каждом листе будем хранить последовательную часть строки и ее длину, а в промежуточных вершинах будем хранить сумму длин всех листьев в поддереве. Изначально дерево состоит из одной вершины {{---}} сама строка. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
==Merge== | ==Merge== | ||
− | Когда приходит запрос на конкатенацию с другой строкой | + | Когда приходит запрос на конкатенацию с другой строкой мы объединяем оба дерева, создав новый корень и подвесив к нему обе строки. |
Пример результата конкатенации двух строк: | Пример результата конкатенации двух строк: | ||
Строка 18: | Строка 10: | ||
===Псевдокод=== | ===Псевдокод=== | ||
− | Node merge(Node | + | Node merge(Node T1, Node T2): |
− | + | return Node(T1, T2, T1.w + T2.w) | |
===Время работы=== | ===Время работы=== | ||
− | Асимптотика выполнения операции конкатенации двух строк | + | Асимптотика выполнения операции конкатенации двух строк очевидно <tex>O(1)</tex>. |
==Получение символа по индексу== | ==Получение символа по индексу== | ||
− | Чтобы получить символ по некоторому индексу <tex>i</tex>, будем спускаться по дереву из корня, используя веса | + | Чтобы получить символ по некоторому индексу <tex>i</tex>, будем спускаться по дереву из корня, используя веса записанные вершинах чтобы определить в какое поддерево пойти из текущей вершины. Алгоритм выглядит следующим образом: |
* Текущая вершина {{---}} не лист, тогда возможно два варианта: | * Текущая вершина {{---}} не лист, тогда возможно два варианта: | ||
** Вес левого поддерева больше либо равен <tex>i</tex>, тогда идем в левое поддерево. | ** Вес левого поддерева больше либо равен <tex>i</tex>, тогда идем в левое поддерево. | ||
** Иначе идем в правое поддерево и ищем там <tex>i - w</tex> символ, где <tex>w</tex> вес левого поддерева. | ** Иначе идем в правое поддерево и ищем там <tex>i - w</tex> символ, где <tex>w</tex> вес левого поддерева. | ||
− | * Текущая вершина {{---}} лист, тогда в этом листе хранится ответ | + | * Текущая вершина {{---}} лист, тогда в этом листе хранится ответ, необходимо взять символ с соответствующим номером у строки которая там хранится. |
===Псевдокод=== | ===Псевдокод=== | ||
− | '''char''' get('''int''' i, Node node): | + | '''char''' get('''int''' i,Node node): |
'''if''' node.left <tex>\ne \varnothing</tex> | '''if''' node.left <tex>\ne \varnothing</tex> | ||
'''if''' node.left.w >= i | '''if''' node.left.w >= i | ||
Строка 46: | Строка 38: | ||
===Время работы=== | ===Время работы=== | ||
− | Асимптотика выполнения одного такого запроса | + | Асимптотика выполнения одного такого запроса очевидно <tex>O(h)</tex>, где <tex>h</tex> {{---}} высота дерева. |
==Split== | ==Split== | ||
− | Чтобы разбить строку на две по некоторому индексу <tex>i</tex> | + | Чтобы разбить строку на две по некоторому индексу <tex>i</tex> необходимо спускаясь по дереву (аналогично операции <tex>\mathrm{get}</tex>), каждую вершину на пути поделить на две, каждая из которых будет соответствовать одно из половинок строк, при этом необходимо после деления пересчитать вес этих вершин. |
Пускай дано дерево: | Пускай дано дерево: | ||
Строка 56: | Строка 48: | ||
[[file:Split1.png|800px|Перед операцией split.]] | [[file:Split1.png|800px|Перед операцией split.]] | ||
− | Тогда результатом выполнения операции <tex>\mathrm{split}</tex> по индексу | + | Тогда результатом выполнения операции <tex>\mathrm{split}</tex> по индексу 16 будет: |
[[file:Split2.png|800px|Результат выполнения операции split.]] | [[file:Split2.png|800px|Результат выполнения операции split.]] | ||
− | Заметим, что появляются лишние вершины, у которых есть только один потомок. От них можно легко | + | Заметим, что появляются лишние вершины, у которых есть только один потомок. От них можно легко избавится, просто заменив их на этого потомка. Тогда результатом той же операции <tex>\mathrm{split}</tex> будет: |
[[file:Split3.png|800px|Результат выполнения операции split.]] | [[file:Split3.png|800px|Результат выполнения операции split.]] | ||
===Псевдокод=== | ===Псевдокод=== | ||
− | + | Pair<Node, Node> split(Node node, '''int''' i): | |
− | Node | + | Node t1, t2 |
+ | t1.w = i | ||
+ | t2.w = node.w - i | ||
+ | '''Pair''' res | ||
'''if''' node.left <tex>\ne \varnothing</tex> | '''if''' node.left <tex>\ne \varnothing</tex> | ||
'''if''' node.left.w >= i | '''if''' node.left.w >= i | ||
res = split(node.left, i) | res = split(node.left, i) | ||
− | + | t1 = res.first | |
− | + | t2.left = res.second | |
− | + | t2.right = node.right | |
− | |||
'''else''' | '''else''' | ||
res = split(node.right, i - node.left.w) | res = split(node.right, i - node.left.w) | ||
− | + | t1.left = node.left | |
− | + | t1.right = res.first | |
− | + | t2 = res.second | |
− | |||
'''else''' | '''else''' | ||
− | + | t1.s = node.s.substr(0, i) | |
− | + | t2.s = node.s.substr(i, s.len) | |
− | + | '''return''' pair(t1, t2) | |
− | |||
− | '''return''' | ||
===Время работы=== | ===Время работы=== | ||
− | Нетрудно заметить | + | Нетрудно заметить что асимптотическая сложность выполнения данной операции <tex>O(h)</tex>, где <tex>h</tex> {{---}} высота дерева. |
==Операции удаления и вставки== | ==Операции удаления и вставки== | ||
− | Нетрудно понять, что имея | + | Нетрудно понять, что имея операция <tex>\mathrm{merge}</tex> и <tex>\mathrm{split}</tex>, можно легко через них выразить операции <tex>\mathrm{delete}</tex> и <tex>\mathrm{insert}</tex> по аналогии с другими деревьями поиска. |
− | Операция <tex>\mathrm{delete}</tex> удаляет из строки подстроку | + | Операция <tex>\mathrm{delete}</tex> удаляет из строки подстроку начиная с индекса <tex>beginIndex</tex> и заканчивая (не включая) индексом <tex>endIndex</tex>. |
===Псевдокод=== | ===Псевдокод=== | ||
Node delete(Node node, '''int''' beginIndex, '''int''' endIndex): | Node delete(Node node, '''int''' beginIndex, '''int''' endIndex): | ||
− | ( | + | Node t1, t2, t3 |
− | + | (t1, t2) = split(node, beginIndex) | |
− | '''return''' merge( | + | t3 = split(t2, endIndex - beginIndex).second |
+ | '''return''' merge(t1, t3) | ||
− | Операция <tex>\mathrm{insert}</tex> вставляет данную строку <tex>s</tex> в исходную | + | Операция <tex>\mathrm{insert}</tex> вставляет данную строку <tex>s</tex> в исходную начиная с позиции <tex>insertIndex</tex>. |
===Псевдокод=== | ===Псевдокод=== | ||
Node insert(Node node, '''int''' insertIndex, '''string''' s): | Node insert(Node node, '''int''' insertIndex, '''string''' s): | ||
− | ( | + | Node t1, t2 |
− | + | (t1, t2) = split(node, insertIndex) | |
− | '''return''' merge( | + | Node t3 = Node(s) |
+ | t1 = merge(t1, t3) | ||
+ | '''return''' merge(t1, t2) | ||
===Время работы=== | ===Время работы=== | ||
− | Так как данные операции используют только <tex>\mathrm{split}</tex> и <tex>\mathrm{merge}</tex> | + | Так как данные операции используют только <tex>\mathrm{split}</tex> и <tex>\mathrm{merge}</tex> то асимптотика времени их работы <tex>O(h)</tex>, где <tex>h</tex> - высота дерева. |
==Балансировка== | ==Балансировка== | ||
− | Для того | + | Для того чтобы дерево не превращалось в бамбук: |
[[file:Bamboo.png|800px|Пример вырождения дерева в бамбук.]] | [[file:Bamboo.png|800px|Пример вырождения дерева в бамбук.]] | ||
Строка 125: | Строка 119: | ||
==Оптимизации== | ==Оптимизации== | ||
− | * Для уменьшения объема памяти | + | * Для уменьшения объема памяти занимаемой деревом и уменьшения высоты дерево, предлагается при конкатенации с маленькой строкой делать конкатенацию классическим способом. |
− | * Кэширование. Так как зачастую нужен последовательный доступ к индексам (например <tex>i</tex> и <tex>i + 1</tex>), то можно запоминать лист | + | * Кэширование. Так как зачастую нужен последовательный доступ к индексам (например <tex>i</tex> и <tex>i + 1</tex>), то можно запоминать лист, а также его границы, в который мы пришли после очередного запроса <tex>get</tex> и на следующем запросе сначала искать в сохраненном листе. |
==См. также== | ==См. также== | ||
Строка 136: | Строка 130: | ||
==Источники информации== | ==Источники информации== | ||
− | *[[wikipedia:en:Rope_(data_structure)| | + | *[[wikipedia:en:Rope_(data_structure)|Википедия {{---}} Rope]] |
*[http://habrahabr.ru/post/144736/ Ropes {{---}} быстрые строки] | *[http://habrahabr.ru/post/144736/ Ropes {{---}} быстрые строки] | ||
*[http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9450&rep=rep1&type=pdf Ropes: an Alternative to Strings] | *[http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9450&rep=rep1&type=pdf Ropes: an Alternative to Strings] |