Изменение списка из заказа A в заказ B с использованием только операций push и remove - PullRequest
4 голосов
/ 03 марта 2010

Существует ли какой-либо известный алгоритм (или очевидное решение) для преобразования списка из порядка A в порядок B, используя только операции push и remove?Например, от a b c d до b c a d можно выполнить с помощью следующих операций:

remove(a)
remove(d)
push(a)
push(d)

Или от c b a до a b будет

remove(c)
remove(b)
push(b)

Илиот a c b до a c b d будет

push(d)

Здесь push добавляет элемент в конец списка, а remove удаляет элемент и сдвигает последующие элементы, так чтосписок всегда находится в непрерывном состоянии (без «пустых слотов»).Кроме того, существует условие, что в любой данный момент список может содержать один и тот же элемент только один раз.Поэтому кажется, что звук сначала выполняет все remove с в одной связке, а затем все push с.Порядок remove s, очевидно, не имеет значения, но порядок push es имеет значение.

Тривиальным решением было бы сначала удалить все элементы, а затем выдвинуть нужные элементы в желаемом порядке.Но так как я знаю, что большую часть времени преобразования будут довольно маленькими, эквивалентными одиночным push es или remove s, я хочу "повторно использовать" любой существующий правильный порядок в списке (например, преобразование abcdef)для abcde потребуется всего одна remove операция - большая разница с альтернативой (6 + 5 операций)).Итак, как найти правильный (минимальный) набор remove с и список push es?

Ответы [ 3 ]

4 голосов
/ 03 марта 2010

Из того, что я могу сказать, вы будете удаляться отовсюду и отталкиваться назад.

Поскольку вы не можете вставить его в середину списка, единственный способ минимизировать количество операций - это линейно проходить по списку и удалять любые неправильные элементы.

т.е. 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 2, 4, 6, 7, 8, 13, 14

  1. сравнить 1
    1. удалить 1
  2. сравнить 2
  3. сравнить 3
    1. удалить 3
  4. сравнить 4
  5. сравнить 5
    1. удалить 5
  6. сравнить 6
  7. сравнить 7
  8. сравнить 8
  9. сравнить 9
    1. удалить 9
  10. сравнить 10
    1. удалить 10 (достигнут конец)
  11. толчок 13
  12. толчок 14

Если бы ваш толчок не был так ограничен, были бы более эффективные способы сделать это.

1 голос
/ 03 марта 2010

Мозговой:

  • Произвольный порядок может рассматриваться как сортировка при условии правильной функции сравнения.
  • A Декартово дерево обладает интересным свойством, заключающимся в том, что оно моделирует как исходный порядок списка (путем обхода по месту), так и отсортированный порядок списка (как куча).

Возможно, вы можете использовать декартово дерево для определения порядка операций следующим образом (отредактировано для исправления ошибок, обнаруженных при работе с примером ниже):

  • Назовите исходный список Л.
  • Построить декартово дерево T из L. Это занимает O (n) времени.
  • Построить пустую очередь приоритетов Q.
  • Назначить узел N = корень T (это самый маленький элемент в списке, еще не обработанный).
  • Повторяйте следующие шаги, пока Q не станет пустым, а N не станет равным null .
    • Удалите всех оставшихся потомков N из L и поместите их в Q. Пункт N теперь находится в положении на L.
    • Назначить N = правый потомок N.
    • Если N <глава Q: уберите N из L и поместите его в Q. </li>
    • Пока голова Q <= N или N == <em>ноль : выведите Q и поместите результат в L.

Давайте попробуем пример:

Я буду использовать пример со страницы Википедии для Декартовых деревьев :

Cartesian Tree for list 9,3,7,1,8,12,10,20,15,18,5

В начале:

  • L = 9,3,7,1,8,12,10,20,15,18,5
  • Q = пусто
  • N = 1

Первый цикл:

  • Переместить левых детей N в Q:
    • L = 1,8,12,10,20,15,5
    • Q = 3,7,9
  • Переназначить N на правильного ребенка: N = 5
  • Переместить N в Q
    • L = 1,8,12,10,20,15
    • Q = 3,5,7,9
  • Нажмите элементы
    • L = 1,8,12,10,20,15,3,5
    • Q = 7,9

Второй цикл:

  • Переместить левых детей N в Q:
    • L = 1,3,5
    • Q = 7,8,9,10,12,15,20
  • Переназначить N на правильного ребенка: N = null
  • Перенесите оставшиеся предметы из Q на L:
    • L = 1,3,5,7,8,9,10,12,15,20

Готово.

1 голос
/ 03 марта 2010

Это не "хорошо известно", а то, что я только что придумал.

  1. Remove все элементы из оригинала, которых нет в нужном списке; добавьте каждую операцию удаления в список операций.
  2. Add любые элементы для работы, которые отсутствуют; добавьте каждую операцию добавления в список операций.
  3. Назначьте каждому элементу исходного списка «номер заказа», который является позицией его аналога в требуемом списке. (см. фрагмент ниже.)
  4. Найдите первый элемент (0 в исходном 1 ) и последний элемент в том порядке (1 в исходном 2 ).
  5. Remove все элементы на позициях за пределами этого диапазона и помещать их в какой-то другой временный список; добавьте каждую операцию удаления в список операций.
  6. Сортировка временного списка по «номеру заказа».
  7. Add элементы в порядке из временного списка; добавьте каждую операцию добавления в список операций.
  8. Убедитесь, что этот вновь преобразованный список соответствует требуемому списку.
  9. Вернуть список операций.

Ex. из шага 2:

2013 1234

abcd bcad

Поскольку вы можете только добавлять и удалять, а не вставлять элементы или добавлять / удалять диапазон, я не думаю, что вы можете сохранить существующий порядок любых подсегментов в порядке , кроме сегмент по порядку от 0. Вот почему все остальные элементы удаляются на шаге 4. Поскольку вам не нужно добавлять эти элементы сразу после удаления (на основе вашего примера), я предполагаю, что их можно где-то хранить. Итак, почему бы не сохранить их в сортируемом списке и отсортировать по назначенному «порядковому номеру» для определения порядка добавления?

Если я не понял ваш вопрос, вас интересует порядок операций добавления / удаления, поэтому вам не нужно фактически преобразовывать список (поскольку у вас уже есть преобразованный список); Вы хотите шаги, чтобы создать преобразованный список. Поэтому каждый раз, когда вы добавляете или удаляете в алгоритме, добавляйте «операцию» в список (очередь) операций (например, operations.Add("remove(a)")). Затем алгоритм возвращает этот список операций.

Я написал возможную реализацию в C #. Я немного проверил (скриншоты ниже), и, похоже, это сработало. Однако это может быть и не самая лучшая реализация, которую кто-то может написать.

public static IEnumerable<string> DetermineTransformOrder<T>(
    IEnumerable<T> original, IEnumerable<T> desired)
{
    // handle the easy case immediately
    if (original.SequenceEqual(desired))
    {
        return new List<string>();
    }

    List<KeyValuePair<int,T>> workingOriginal = new List<KeyValuePair<int,T>>();
    List<T> workingDesired = desired.ToList();
    List<string> operations = new List<string>(); //using Queue<> is ok too

    // load workingOriginal
    foreach(T element in original)
        workingOriginal.Add(new KeyValuePair<int, T>(workingDesired.IndexOf(element), element));

    //1. Remove all elements from the original that are not in the desired list; 
    //   add each remove operation to the list of operations.
    var tempWorking = new List<KeyValuePair<int,T>>(workingOriginal);
    foreach (var element in tempWorking)
    {
        if (!workingDesired.Contains(element.Value))
        {
            workingOriginal.Remove(element);
            operations.Add("remove(" + element.Value.ToString() + ")");
        }
    }

    //2. Add any elements to working that are missing;
    //   add each add operation to the list of operations.
    tempWorking = new List<KeyValuePair<int,T>>(workingOriginal);
    foreach(T element in workingDesired)
    {
        if(!workingOriginal.Exists(x=>x.Value.Equals(element)))
        {
            workingOriginal.Add(new KeyValuePair<int, T>(workingDesired.IndexOf(element), element));
            operations.Add("add("+element+")");
        }
    }

    //3. Assign to each element of the original list a "order number" 
    //   which is the position of its analog in the desired list.

    // note: already done above. would have done it here, but
    //       KeyValuePair.Key is read-only.

    //4. Find the first element (0 at original[1]) and the 
    //   last element in order from there (1 at original[2]).
    int indexOfFirstElement = workingOriginal.FindIndex(x => x.Key == 0);
    int indexOfLastElement = indexOfFirstElement;

    // move to index of last in-order element
    for (int i = indexOfLastElement + 1;
         i < workingOriginal.Count &&
         workingOriginal[i - 1].Key + 1 == workingOriginal[i].Key;
         i++, indexOfLastElement++) ;

    //5. Remove all elements at positions outside of this range and put them in some
    //   other temporary list; add each remove operation to the list of operations.
    List<KeyValuePair<int, T>> temporary = new List<KeyValuePair<int, T>>();
    var inOrderElements = workingOriginal.GetRange(
                            indexOfFirstElement, 
                            indexOfLastElement - indexOfFirstElement + 1);
    var outOfOrderElements = new List<KeyValuePair<int, T>>(
                                workingOriginal.Except(inOrderElements));

    foreach (var element in outOfOrderElements)
    {
        workingOriginal.Remove(element);
        temporary.Add(element);
        operations.Add("remove(" + element.Value.ToString() + ")");
    }

    //6. Sort the temporary list based on the "order number".
    temporary.Sort((x, y) => { return x.Key.CompareTo(y.Key); });

    //7. Add the elements in order from the temporary list; 
    //   add each add operation to the list of operations.
    foreach (var element in temporary)
    {
        workingOriginal.Add(element);
        operations.Add("add(" + element.Value.ToString() + ")");
    }

    //8. Verify that this newly transformed list matches the desired list.
    var newlyTransformed = workingOriginal.ConvertAll<T>(x => { return x.Value; });
    if (!newlyTransformed.SequenceEqual(desired))
    {
        // this should never happen
        throw new StackOverflowException("FAILED");
    }

    //9. Return the list of operations.
    return operations;
}

alt text alt text alt text

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...