代码之家  ›  专栏  ›  技术社区  ›  Joonas Pulakka

仅使用“推”和“删除”操作将列表从订单A修改为订单B

  •  4
  • Joonas Pulakka  · 技术社区  · 14 年前

    是否有任何已知的算法(或明显的解决方案)用于仅使用 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)
    

    在这里, 将元素追加到列表末尾,然后 去除 移除元素并移动后续元素,使列表始终处于连续状态(没有“空槽”)。另外,还有一个条件,在任何给定的时刻,列表只能包含同一个元素一次。因此,首先要做的似乎是 去除 一堆的,所有的 在那之后。秩序 去除 显然不重要,但是 ES确实如此。

    一个简单的解决方案是首先删除所有元素,然后按所需的顺序推送所需的元素。但是,因为我知道大多数时候转换会很小,相当于单个 ES或 去除 s,我想“重用”列表中任何现有的正确顺序(例如,转换 abcdef abcde 只需要一个 去除 操作-与备选方案(6+5操作)大不相同。那么,如何找到 去除 S和列表 锿?

    3 回复  |  直到 14 年前
        1
  •  4
  •   Benjamin Manns    14 年前

    据我所知,你将从任何地方离开,然后推到后面。

    因为不能插入到列表中间,所以可以最小化操作数的唯一方法是线性遍历列表并删除任何不正确的元素。

    即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

    如果你的努力没有那么有限,那么就有更有效的方法来做到这一点。

        2
  •  1
  •   Community rohancragg    7 年前

    这不是“广为人知”,而是我刚刚想到的。

    1. remove all elements from the original that are not in the desired list;add each remove operation to the list of operations.
    2. add any elements to working that are missing;add each add operation to the list of operations.
    3. 为原始列表的每个元素分配一个“订单号”,即其模拟在所需列表中的位置。(请参见下面的代码段。)
    4. 查找第一个元素(0 at original 1 )和顺序中的最后一个元素(1 at original 2 ).
    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.
    6. 根据“订单号”对临时列表进行排序。
    7. add The elements in order from the temporary list;add each add operation to the list of operations.
    8. 验证此新转换的列表是否与所需的列表匹配。
    9. 返回操作列表。
    < Buff行情>

    例如,从步骤2开始:

    2013年1234

    bcad密件抄送

    < /块引用>

    因为您只能添加和删除--不能插入元素或添加/删除范围--我认为您不能保留任何顺序子段 other than in order segment from 0的现有顺序。这就是为什么在步骤4中删除所有其他元素的原因。因为您不必在删除后立即添加这些元素(基于您的示例),所以我假设可以将它们存储在某个地方。那么,为什么不将它们存储在可排序列表中,并根据分配的“订单号”进行排序以确定添加的顺序呢?

    除非我误解了您的问题,否则您对添加/删除操作的顺序感兴趣,因此您不关心实际转换列表(因为您已经转换了列表);您需要创建转换列表的步骤。因此,每次在算法中添加或删除时,都会将“operation”添加到操作(例如 operations.add(“remove(a)”) )的列表(队列)中。然后算法返回此操作列表。

    我用C编写了一个可能的实现。我做了一点测试(下面的屏幕截图),它似乎起作用了。然而,它也可能不是有人可以编写的最佳实现。

    公共静态IEnumerable<string>DetermineTransformOrder<t>。(
    IEnumerable<t>原始,需要IEnumerable<t>)
    {
    //立即处理简单的案例
    if(原始序列相等(所需)
    {
    返回新列表<string>();
    }
    
    list<keyValuePair<int,t>>workingOriginal=新列表<keyValuePair<int,t>>();
    list<t>workingDesired=desired.tolist();
    list<string>operations=new list<string>();//使用队列也可以
    
    //加载WorkingOriginal
    foreach(原始的t元素)
    workingOriginal.add(new keyValuePair<int,t>(workingDesired.indexof(element),element));
    
    //1。从原始列表中删除不在所需列表中的所有元素;
    //将每个移除操作添加到操作列表中。
    var tempworking=新列表<keyValuePair<int,t>>(WorkingOriginal);
    foreach(模板中的var元素)
    {
    如果(!)工作所需。包含(element.value)
    {
    WorkingOriginal.Remove(元素);
    operations.add(“remove(+element.value.toString()+”);
    }
    }
    
    / / 2。向缺少的工作添加任何元素;
    //将每个添加操作添加到操作列表中。
    tempworking=新列表<keyValuePair<int,t>>(WorkingOriginal);
    foreach(所需工作中的t元素)
    {
    如果(!WorkingOriginal.exists(x=>x.Value.Equals(element)))
    {
    workingOriginal.add(new keyValuePair<int,t>(workingDesired.indexof(element),element));
    operations.add(“添加(+element+”);
    }
    }
    
    / / 3。为原始列表的每个元素分配一个“订单号”
    //所需列表中的模拟位置。
    
    //注意:已经完成了。本来可以在这里做的,但是
    //keyValuePair.key是只读的。
    
    / / 4。找到第一个元素(0在原始的[1]处),然后
    //按顺序排列的最后一个元素(原始[2]处为1)。
    int indexoffirstelement=workingoriginal.findindex(x=>x.key==0);
    int indexoflastelement=indexoffirstelement;
    
    //移到最后一个有序元素的索引
    对于(int i=indexoflastelement+1;
    I<WorkingOriginal.Count&
    workingoriginal[i-1].key+1==workingoriginal[i].key;
    i++,indexoflastelement++);
    
    //5。移除此范围之外位置的所有元素,并将它们放入一些
    //其他临时列表;将每个移除操作添加到操作列表中。
    list<keyValuePair<int,t>>temporary=新列表<keyValuePair<int,t>>();
    var inorderences=WorkingOriginal.getRange(
    耐火指数,
    lastelement的indexoffirstelement+1);
    var outoforderements=新列表<keyValuePair<int,t>。(
    除(无序)外,工作原始;
    
    foreach(无序中的var元素)
    {
    WorkingOriginal.Remove(元素);
    临时添加(元素);
    operations.add(“remove(+element.value.toString()+”);
    }
    
    //6。根据“订单号”对临时列表进行排序。
    temporary.sort((x,y)=>返回x.key.compareto(y.key););
    
    / / 7。从临时列表中按顺序添加元素;
    //将每个添加操作添加到操作列表中。
    foreach(临时变量元素)
    {
    workingoriginal.add(元素);
    operations.add(“add(+element.value.toString()+”);
    }
    
    / / 8。验证此新转换的列表是否与所需的列表匹配。
    var newlytransformed=workingoriginal.convertall<t>(x=>返回x.value;);
    如果(!)newlyTransformed.SequenceEqual(所需)
    {
    //这不应该发生
    引发新的StackOverflowException(“失败”);
    }
    
    / / 9。返回操作列表。
    返回操作;
    }
    < /代码> 
    
    

    从不在所需列表中的原始列表中添加;将每个移除操作添加到操作列表中。
  • Add缺少的任何工作元素;将每个添加操作添加到操作列表中。
  • 为原始列表的每个元素分配一个“订单号”,即其模拟在所需列表中的位置。(请参见下面的代码段。)
  • 查找第一个元素(0在原始1)最后一个元素按顺序排列(1个在原始2)
  • 删除位于该范围之外位置的所有元素,并将它们放入其他临时列表中;将每个移除操作添加到操作列表中。
  • 根据“订单号”对临时列表进行排序。
  • 添加从临时列表中按顺序排列的元素;将每个添加操作添加到操作列表中。
  • 验证此新转换的列表是否与所需的列表匹配。
  • 返回操作列表。
  • 例如,从步骤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

        3
  •  1
  •   Community rohancragg    7 年前

    头脑风暴:

    也许您可以使用笛卡尔树来确定操作顺序,如下所示(在下面的示例中编辑以修复发现的错误):