Персистентный стек — различия между версиями
Yurik (обсуждение | вклад) |
Yurik (обсуждение | вклад) |
||
Строка 19: | Строка 19: | ||
Тогда операции push и pop будут иметь следующий вид:<br> | Тогда операции push и pop будут иметь следующий вид:<br> | ||
− | * <tex>push(x | + | * <tex>push(i, x)</tex> — добавляет элемент х к стеку с номером i, результирующий стек будет иметь номер n + 1. |
* <tex>pop(i)</tex> — возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него. | * <tex>pop(i)</tex> — возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него. | ||
− | |||
результирующий стек будет иметь номер n + 1. | результирующий стек будет иметь номер n + 1. | ||
− | |||
== Пример == | == Пример == | ||
− | Пусть изначально у нас есть один пустой стек. | + | Пусть изначально у нас есть один пустой стек. Запишем его в массив. |
<br> | <br> | ||
− | + | {| border = 1; cellspacing = 0; class="wikitable" | |
+ | |- align = "center" | ||
+ | !index | ||
+ | |1 | ||
+ | |-align = "center" | ||
+ | !value | ||
+ | |<tex>null</tex> | ||
+ | |-align = "center" | ||
+ | !prev | ||
+ | |<tex>null</tex> | ||
+ | |} | ||
+ | |||
<br> | <br> | ||
<br> | <br> | ||
− | Далее выполним <tex>push(1, | + | Далее выполним <tex>push(1, 3)</tex>. Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2ю ячейку массива: |
<br> | <br> | ||
− | + | {| border = 1; cellspacing = 0; class="wikitable" | |
+ | |- align = "center" | ||
+ | !index | ||
+ | |1 | ||
+ | | 2 | ||
+ | |-align = "center" | ||
+ | !value | ||
+ | |<tex>null</tex> | ||
+ | |3 | ||
+ | |-align = "center" | ||
+ | !prev | ||
+ | |<tex>null</tex> | ||
+ | |1 | ||
+ | |} | ||
+ | |||
<br> | <br> | ||
− | Аналогично выполним <tex>push(2, | + | Аналогично выполним <tex>push(2, 5)</tex>: |
− | |||
− | |||
<br> | <br> | ||
+ | {| border = 1; cellspacing = 0; class="wikitable" | ||
+ | |- align = "center" | ||
+ | !index | ||
+ | |1 | ||
+ | | 2 | ||
+ | | 3 | ||
+ | |-align = "center" | ||
+ | !value | ||
+ | |<tex>null</tex> | ||
+ | |3 | ||
+ | |5 | ||
+ | |-align = "center" | ||
+ | !prev | ||
+ | |<tex>null</tex> | ||
+ | |1 | ||
+ | |2 | ||
+ | |} | ||
− | + | <br> | |
+ | <br> | ||
<br> | <br> | ||
− | * <tex>pop( | + | * Выполним <tex>pop(3)</tex>. он возвращает 5 и копирует 2ю вершину. |
<br> | <br> | ||
− | + | {| border = 1; cellspacing = 0; class="wikitable" | |
+ | |- align = "center" | ||
+ | !index | ||
+ | |1 | ||
+ | | 2 | ||
+ | | 3 | ||
+ | | 4 | ||
+ | |-align = "center" | ||
+ | !value | ||
+ | |<tex>null</tex> | ||
+ | |3 | ||
+ | |5 | ||
+ | |3 | ||
+ | |-align = "center" | ||
+ | !prev | ||
+ | |<tex>null</tex> | ||
+ | |1 | ||
+ | |2 | ||
+ | |1 | ||
+ | |} | ||
<br><br> | <br><br> | ||
− | + | Так будет выглядеть массив после последовательности операций <tex>push(3, 6), push(5, 1), pop(4), pop(5), push(7, 9):</tex> | |
− | < | + | |
− | + | {| border = 1; cellspacing = 0; class="wikitable" | |
+ | |- align = "center" | ||
+ | !index | ||
+ | |1 | ||
+ | | 2 | ||
+ | | 3 | ||
+ | | 4 | ||
+ | | 5 | ||
+ | | 6 | ||
+ | | 7 | ||
+ | | 8 | ||
+ | | 9 | ||
+ | |-align = "center" | ||
+ | !value | ||
+ | |<tex>null</tex> | ||
+ | |3 | ||
+ | |5 | ||
+ | |3 | ||
+ | |6 | ||
+ | |1 | ||
+ | |<tex>null</tex> | ||
+ | |5 | ||
+ | |9 | ||
+ | |-align = "center" | ||
+ | !prev | ||
+ | |<tex>null</tex> | ||
+ | |1 | ||
+ | |2 | ||
+ | |1 | ||
+ | |3 | ||
+ | |5 | ||
+ | |<tex>null</tex> | ||
+ | |2 | ||
+ | |7 | ||
+ | |} | ||
<br> | <br> | ||
Версия 16:20, 7 июня 2012
Определение: |
Персистентными структурами данных называются такие структуры, хранящие все свои промежуточные версии. |
Рассмотрим такую структуру на примере стека.
Наивная реализация
Самое простое и очевидное решение этой задачи — честное копирование стека при каждой операции.
Очевидно, что это не самое эффективное решение. Сложность одной операции составляет и количество требуемой памяти — .
Эффективная реализация
Попробуем решить задачу эффективнее. Заведем массив запросов, модифицирующих стек.
У каждого элемента массива будет 2 поля: значение в вершине стека и индекс предыдущей версии стека.
Тогда операции push и pop будут иметь следующий вид:
- — добавляет элемент х к стеку с номером i, результирующий стек будет иметь номер n + 1.
- — возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него.
результирующий стек будет иметь номер n + 1.
Пример
Пусть изначально у нас есть один пустой стек. Запишем его в массив.
index | 1 |
---|---|
value | |
prev |
Далее выполним . Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2ю ячейку массива:
index | 1 | 2 |
---|---|---|
value | 3 | |
prev | 1 |
Аналогично выполним :
index | 1 | 2 | 3 |
---|---|---|---|
value | 3 | 5 | |
prev | 1 | 2 |
- Выполним . он возвращает 5 и копирует 2ю вершину.
index | 1 | 2 | 3 | 4 |
---|---|---|---|---|
value | 3 | 5 | 3 | |
prev | 1 | 2 | 1 |
Так будет выглядеть массив после последовательности операций
index | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
value | 3 | 5 | 3 | 6 | 1 | 5 | 9 | ||
prev | 1 | 2 | 1 | 3 | 5 | 2 | 7 |
В итоге мы имеем доступ ко всем версиям стека за
времени и памяти (массив длины n и n самих "стеков").