Jump to content

Веревка (структура данных)

Простая веревка, построенная на веревке «Hello_my_name_is_Simon».

В компьютерном программировании веревка меньшего размера , или шнур — это структура данных, состоящая из строк которая используется для эффективного хранения и управления очень длинной строкой. Например, программа редактирования текста может использовать веревку для представления редактируемого текста, чтобы можно было эффективно выполнять такие операции, как вставка, удаление и произвольный доступ. [1]

Описание

[ редактировать ]

Веревка — это тип двоичного дерева , в котором каждый лист (конечный узел) содержит строку и длину (также известную как «вес»), а каждый узел, расположенный дальше по дереву, содержит сумму длин всех листьев в его левое поддерево . Таким образом, узел с двумя дочерними элементами делит всю строку на две части: в левом поддереве хранится первая часть строки, в правом поддереве хранится вторая часть строки, а вес узла равен длине первой части.

Для операций с веревками строки, хранящиеся в узлах, считаются постоянными неизменяемыми объектами в типичном неразрушающем случае, что допускает некоторое поведение копирования при записи . Листовые узлы обычно реализуются как базовые строки фиксированной длины со счетчиком ссылок, присоединяемым для освобождения, когда они больше не нужны, хотя сборки мусора можно использовать и другие методы .

Операции

[ редактировать ]

В следующих определениях N — длина веревки, то есть вес корневого узла.

Собирать листья

[ редактировать ]
Определение: стек S и список L. Создайте Пройдите вниз по самому левому позвоночнику дерева, пока не дойдете до листа l', добавляя каждый узел n к S . к L. Добавьте l ' Родитель l' ( p ) находится на вершине стека. Повторите процедуру для правого поддерева p.
final class InOrderRopeIterator implements Iterator<RopeLike> {

    private final Deque<RopeLike> stack;

    InOrderRopeIterator(@NonNull RopeLike root) {
        stack = new ArrayDeque<>();
        var c = root;
        while (c != null) {
            stack.push(c);
            c = c.getLeft();
        }
    }

    @Override
    public boolean hasNext() {
        return stack.size() > 0;
    }

    @Override
    public RopeLike next() {
        val result = stack.pop();

        if (!stack.isEmpty()) {
            var parent = stack.pop();
            var right = parent.getRight();
            if (right != null) {
                stack.push(right);
                var cleft = right.getLeft();
                while (cleft != null) {
                    stack.push(cleft);
                    cleft = cleft.getLeft();
                }
            }
        }

        return result;
    }
}

Ребалансировка

[ редактировать ]
Определение: Соберите набор листьев L и перестройте дерево снизу вверх.
static boolean isBalanced(RopeLike r) {
    val depth = r.depth();
    if (depth >= FIBONACCI_SEQUENCE.length - 2) {
        return false;
    }
    return FIBONACCI_SEQUENCE[depth + 2] <= r.weight();
}

static RopeLike rebalance(RopeLike r) {
    if (!isBalanced(r)) {
        val leaves = Ropes.collectLeaves(r);
        return merge(leaves, 0, leaves.size());
    }
    return r;
}

static RopeLike merge(List<RopeLike> leaves) {
    return merge(leaves, 0, leaves.size());
}

static RopeLike merge(List<RopeLike> leaves, int start, int end) {
    int range = end - start;
    if (range == 1) {
        return leaves.get(start);
    }
    if (range == 2) {
        return new RopeLikeTree(leaves.get(start), leaves.get(start + 1));
    }
    int mid = start + (range / 2);
    return new RopeLikeTree(merge(leaves, start, mid), merge(leaves, mid, end));
}

Вставлять

[ редактировать ]
Определение: Insert(i, S’): вставьте строку S', начинающуюся с позиции i в строку s , чтобы сформировать новую строку C 1 , ..., C i , S' , C i + 1 , ..., C m .
Временная сложность: .

Эту операцию можно выполнить с помощью Split() и два Concat() операции. Стоимость представляет собой сумму трех.

public Rope insert(int idx, CharSequence sequence) {
    if (idx == 0) {
        return prepend(sequence);
    }
    if (idx == length()) {
        return append(sequence);
    }
    val lhs = base.split(idx);
    return new Rope(Ropes.concat(lhs.fst.append(sequence), lhs.snd));
}
Рисунок 2.1: Пример поиска по индексу веревки.
Определение: Index(i): вернуть символ в позицию i
Временная сложность:

Чтобы получить i -й символ, мы начинаем рекурсивный поиск с корневого узла:

@Override
public int indexOf(char ch, int startIndex) {
    if (startIndex > weight) {
        return right.indexOf(ch, startIndex - weight);
    }
    return left.indexOf(ch, startIndex);
}

Например, чтобы найти символ по адресу i=10 на рисунке 2.1, показанном справа, начните с корневого узла (A), обнаружите, что 22 больше 10 и существует левый дочерний элемент, поэтому перейдите к левому дочернему узлу (B). 9 меньше 10, поэтому вычтите 9 из 10 (оставив i=1) и идите к правому ребенку (D). Затем, поскольку 6 больше 1 и есть левый дочерний элемент, перейдите к левому дочернему элементу (G). 2 больше 1, и есть левый дочерний элемент, поэтому снова перейдите к левому дочернему элементу (J). Наконец, 2 больше 1, но левого дочернего элемента нет, поэтому ответом является символ с индексом 1 короткой строки «na» (т. е. «n»). (индекс на основе 1)

Рисунок 2.2: Объединение двух дочерних веревок в одну.
Определение: Concat(S1, S2): объединить две веревки S 1 и S 2 в одну веревку.
Временная сложность: (или время для вычисления веса корня)

Конкатенацию можно выполнить, просто создав новый корневой узел с влево = S1 и right = S2 , что является постоянным временем. Вес родительского узла равен длине левого дочернего узла S 1 , что потребует время, если дерево сбалансировано.

Поскольку для большинства операций с канатами требуются сбалансированные деревья, после объединения может потребоваться повторная балансировка дерева.

Расколоть

[ редактировать ]
Рисунок 2.3: Разделение веревки пополам.
Определение: Split (i, S): разделить строку S на две новые строки S 1 и S 2 , S 1 = C 1 , ..., C i и S 2 = C i + 1 , ..., C m .
Временная сложность:

Есть два случая, которые необходимо рассмотреть:

  1. Точка разделения находится в конце строки (т.е. после последнего символа листового узла).
  2. Точка разделения находится в середине строки.

Второй случай сводится к первому путем разделения строки в точке разделения для создания двух новых конечных узлов, а затем создания нового узла, который является родительским для двух составляющих строк.

Например, чтобы разделить 22-символьную веревку, изображенную на рис. 2.3, на две равные составляющие веревки длиной 11, запросите 12-й символ, чтобы найти узел K на нижнем уровне. Удалите связь K и G. между к родителю G и вычтите вес K из веса D. Перейдите Поднимитесь по дереву и удалите все правые ссылки на поддеревья, охватывающие символы после позиции 11, вычитая вес K из их родительских узлов (в данном случае только узлов D и A ). Наконец, создайте новые осиротевшие узлы K и H, их вместе и создав новый родительский P с весом, равным длине левого узла K. объединив

Поскольку для большинства операций с веревками требуются сбалансированные деревья, после раскола может потребоваться повторная балансировка дерева.

public Pair<RopeLike, RopeLike> split(int index) {
    if (index < weight) {
        val split = left.split(index);
        return Pair.of(rebalance(split.fst), rebalance(new RopeLikeTree(split.snd, right)));
    } else if (index > weight) {
        val split = right.split(index - weight);
        return Pair.of(rebalance(new RopeLikeTree(left, split.fst)), rebalance(split.snd));
    } else {
        return Pair.of(left, right);
    }
}
Определение: Delete(i, j): удалить подстроку C i , …, C i + j − 1 из s , чтобы сформировать новую строку C 1 , …, C i − 1 , C i + j , …, C m .
Временная сложность: .

Эту операцию можно выполнить вдвоем. Split() и один Concat() операция. Сначала разделите веревку на три части, разделенные на i -й и i+j -й символы соответственно, в результате чего строка, подлежащая удалению, будет выделена в отдельный узел. Затем объедините два других узла.

@Override
public RopeLike delete(int start, int length) {
    val lhs = split(start);
    val rhs = split(start + length);
    return rebalance(new RopeLikeTree(lhs.fst, rhs.snd));
}
Определение: Report(i, j): вывести строку C i , …, C i + j − 1 .
Временная сложность:

Чтобы сообщить строку C i , …, C i + j − 1 , найдите узел u , который содержит C i и weight(u) >= j, а затем пройдите T, начиная с узла u . Выведите C i , …, C i + j − 1, выполнив упорядоченный обход T , начиная с узла u .

Сравнение с монолитными массивами

[ редактировать ]
Сложность [ нужна ссылка ]
Операция Веревка Нить
Индекс [1] О(вход) О(1)
Расколоть [1] О(вход) О(1)
Объединить O(1) амортизируется, O(log n) в худшем случае [ нужна ссылка ] На)
Перебирать каждый символ [1] На) На)
Вставлять [2] [ не удалось пройти проверку ] О(вход) На)
Добавить [2] [ не удалось пройти проверку ] O(1) амортизируется, O(log n) в худшем случае O(1) амортизируется, O(n) в худшем случае
Удалить О(вход) На)
Отчет О (j + журнал n) О (дж)
Строить На) На)

Преимущества:

  • Веревки позволяют гораздо быстрее вставлять и удалять текст, чем монолитные строковые массивы, операции с которыми имеют временную сложность O(n).
  • Веревки не требуют дополнительной памяти O(n) при работе (массивам это необходимо для операций копирования).
  • Веревки не требуют больших непрерывных пространств памяти.
  • Если используются только неразрушающие версии операций, верёвка является постоянной структурой данных . В примере программы редактирования текста это приводит к простой поддержке нескольких уровней отмены .

Недостатки:

  • Большее общее использование пространства, когда оно не используется, в основном для хранения родительских узлов. Существует компромисс между тем, какая часть общей памяти занимает такие накладные расходы, и как долго фрагменты данных обрабатываются как строки. Строки в примерах выше нереально короткие для современных архитектур. Накладные расходы всегда равны O(n), но константу можно сделать сколь угодно маленькой.
  • Увеличение времени на управление дополнительным хранилищем
  • Повышенная сложность исходного кода; больший риск ошибок

В этой таблице сравниваются алгоритмические характеристики реализаций струн и веревок, а не их чистая скорость . Строки на основе массива требуют меньших затрат, поэтому (например) операции конкатенации и разделения выполняются быстрее на небольших наборах данных. Однако когда строки на основе массива используются для более длинных строк, временная сложность и использование памяти для вставки и удаления символов становятся неприемлемо большими. Напротив, структура данных типа «веревка» имеет стабильную производительность независимо от размера данных. Кроме того, пространственная сложность для веревок и массивов равна O (n). Таким образом, веревки предпочтительнее, когда данные большие и часто изменяются.

См. также

[ редактировать ]
  • Среда программирования Cedar , в которой веревки использовались «почти с момента ее создания». [1]
  • Анфилада Model T , аналогичная структура данных начала 1970-х годов.
  • Буфер пробела — структура данных, обычно используемая в текстовых редакторах, которая позволяет эффективно выполнять операции вставки и удаления, сгруппированные в одном и том же месте.
  • Таблица частей — еще одна структура данных, обычно используемая в текстовых редакторах.
  1. ^ Jump up to: Перейти обратно: а б с д и Бём, Ханс-Дж; Аткинсон, Расс; Пласс, Майкл (декабрь 1995 г.). «Веревки: альтернатива струнам» ( PDF ) . Программное обеспечение: практика и опыт . 25 (12). Нью-Йорк, штат Нью-Йорк, США: John Wiley & Sons, Inc.: 1315–1330. дои : 10.1002/спе.4380251203 . Архивировано из оригинала 8 марта 2020 г.
  2. ^ Jump up to: Перейти обратно: а б «Обзор реализации веревки» . www.sgi.com . Архивировано из оригинала 19 декабря 2017 г. Проверено 01 марта 2017 г.
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 7f20d0b56fbaab64d091588108391d90__1717140300
URL1:https://arc.ask3.ru/arc/aa/7f/90/7f20d0b56fbaab64d091588108391d90.html
Заголовок, (Title) документа по адресу, URL1:
Rope (data structure) - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)