Python 将列表中项目的子列表移动到新位置

Python 将列表中项目的子列表移动到新位置,python,algorithm,list,Python,Algorithm,List,我解决这个问题已经有一段时间了,在谷歌上搜索了很多次,但从来没有找到一个令人满意的、干净的解决方案。这个问题似乎很简单,解决方案似乎总是非常接近,但到目前为止,我还没有找到 问题 考虑一个从0索引的远程存储项列表。列表的大小未知(或不重要),对列表中的项重新排序的唯一方法是使用以下形式的命令将它们从源索引逐个移动到目标索引: moveitem(from_index, to_index) 对于列表lst,它在概念上等同于Python: lst.insert(to_index, lst.pop(f

我解决这个问题已经有一段时间了,在谷歌上搜索了很多次,但从来没有找到一个令人满意的、干净的解决方案。这个问题似乎很简单,解决方案似乎总是非常接近,但到目前为止,我还没有找到

问题 考虑一个从0索引的远程存储项列表。列表的大小未知(或不重要),对列表中的项重新排序的唯一方法是使用以下形式的命令将它们从源索引逐个移动到目标索引:

moveitem(from_index, to_index)
对于列表lst,它在概念上等同于Python:

lst.insert(to_index, lst.pop(from_index))
任务是编写一个函数

moveitems(from_indices, to_index)
鉴于:

  • from_索引-项目索引列表,以及
  • to_索引-目标索引
生成一系列moveitem(from_index,to_index)指令,将指定索引处的项目移动到列表中的目标位置,保留索引在from_index中列出的顺序

如果远程列表是lst,那么调用moveitems(从_索引到_索引)的净效果应该与以下Python表达式(expr1)等效:

或者,给出列表差异的以下定义:

diff = lambda l1, l2: [x for x in l1 if x not in l2]
所需的结果是(expr2):

因此,如果远程列表包含项目:

[0,1,2,3,4,5,6,7,8,9,10,11,12, ... ]
调用moveitems([8,2,7,4,0,6])应将其转换为:

[1, 3, 5, 8, 2, 7, 4, 0, 6, 9, 10, 11, 12, ... ]
使用以下或等效的移动指令序列:

moveitem(0,5)
moveitem(3,4)
moveitem(7,4)
moveitem(1,3)
moveitem(8,3)
请注意,在上面的示例中,项目的移动顺序与它们在索引列表中出现的顺序相反(从_索引)。这是可以接受的,只要项目在from_索引中列出的顺序在转换后的列表中保持不变

问题是如何计算移动指令的顺序,跟踪列表索引随着项目的连续提取和插入而变化

各种各样的解决办法 目前,我使用的解决方案如下(在Python中):

它首先构造一个索引列表lst=[0,1,2,3,4,…]。我们不知道远程列表的长度,但它必须至少与列表中出现的最大索引(从_索引到_索引)一样长

然后我们迭代要移动的项(从_索引)-每次,项都是要移动的项,最后一个是先前移动的项。其思想是从列表中的任何位置弹出项目,并将其插入到最后移动的项目旁边。由于Python中的列表插入在当前插入点的项之前插入项,因此我们以相反的顺序遍历from_索引。我们在列表lst中搜索项目和最后一个项目,以获得其位置source和dest,然后从positionsource中弹出项目,并将其插入到positiondest中先前移动的项目之前。当目的地高于震源时,需要进行小的校正,以补偿震源弹出时目的地下移1

此解决方案有效——从某种意义上说,它生成的列表与上面(expr1)和(expr2)生成的列表相同——但存在3个主要问题:

  • 它是丑陋的
  • 它是丑陋的;及
  • 这对于大型指数是不切实际的
由于该算法构造了一个与最大感兴趣的索引一样长的临时索引列表,因此即使在较大的列表中移动一些项,也会产生相当大的空间和时间开销。试想一下:

moveitems([56, 12, 19], 1000000)
挑战 我正在寻找一种算法,可以直接计算所需的moveitem指令序列,在项目移动时正确跟踪索引的变化,而无需操作和重复搜索(可能非常大)临时索引列表。如果算法按照列表中列出的相同顺序移动项目,则可获得额外积分

上下文
这些项表示远程服务器上的文档。用户可以浏览列表,选择文档的随机子列表,然后将它们拖动到列表中的新位置。然后,客户机必须向服务器发送一系列moveitem指令,以相应地重新排列列表。文档必须保留所选的顺序(而不是原始顺序)。

考虑要移动的项目、
J
下一个相邻项目和
N
您的目标
idx

两个案例:

  • 案例1:项目位于
    N
I+1
N
(包括)之间的所有元素都被翻译到左侧

  • 案例2:项目位于
    N
N+1
I-1
之间的所有元素都向右平移

此时,近似的伪代码(关于索引)如下所示:

while el = L.pop()
    moveItem(el, N)
    if el < N
        for all indices of L > el and el < N
            indices--
    else
        for all indices of L < el and > N
            indices++
    N+=1 #target our last elem
而el=L.pop()
移动项目(标高,N)
如果elel和elN的所有指数
指数++
N+=1#瞄准我们的最后一个元素

在一个(假定的)适当的implem之下


def move_item(lst, src, dst):
    print('move ', src, dst)
    el = lst.pop(src)
    lst.insert(dst, el)

def move_items(arr, indices, N):
    while(len(indices)):
        el = indices.pop(0)
        if el == N:
            #our elem is in place
            #target is the elem after
            N += 1
            continue

        if el < N:
            #pop effect
            N -=1

            #insert the elem after N (
            move_item(arr, el, N)
            for j in range(len(indices)):
                el2 = indices[j]
                if el2 > el and el2 <= N:
                    indices[j] -= 1
        else:
            move_item(arr, el, N)
            for j in range(len(indices)):
                el2 = indices[j]
                if el2 > N and el2 < el:
                    indices[j] += 1

        #we have inserted an elem after N
        #next target is our last elem
        N += 1
        print('now', arr, 'indices', indices)


move_items([0,1,2,3,4,5,6,7,8,9,10,11,12], [8,2,7,4,0],6)    #[1, 3, 5, 8, 2, 7, 4, 0, 6, 9, 10, 11, 12]
move_items([0,1,2,3,4,5,6,7,8,9,10,11,12], [8,2,7,4,0,10],6) #[1, 3, 5, 8, 2, 7, 4, 0, 10, 6, 9, 11, 12]
move_items([0,1,2,3,4,5,6,7,8,9,10,11,12], [1,10,5,7,3,8],6) #[0, 2, 3, 4, 1, 10, 5, 6, 7, 8, 9, 11, 12]

def移动项目(lst、src、dst):
打印('move',src,dst)
el=第一个波普(src)
第一次插入(dst,el)
def移动项目(arr、索引、N):
而(len(index)):
el=指数pop(0)
如果el==N:
#我们的元素就位了
#目标是后面的元素
N+=1
持续
如果elel和el2 N和el2moveitems([56, 12, 19], 1000000)
    I|J| | | |N
    J| | | |N|I
    N| | | |I|J
    N|I| | | |J
while el = L.pop()
    moveItem(el, N)
    if el < N
        for all indices of L > el and el < N
            indices--
    else
        for all indices of L < el and > N
            indices++
    N+=1 #target our last elem

def move_item(lst, src, dst):
    print('move ', src, dst)
    el = lst.pop(src)
    lst.insert(dst, el)

def move_items(arr, indices, N):
    while(len(indices)):
        el = indices.pop(0)
        if el == N:
            #our elem is in place
            #target is the elem after
            N += 1
            continue

        if el < N:
            #pop effect
            N -=1

            #insert the elem after N (
            move_item(arr, el, N)
            for j in range(len(indices)):
                el2 = indices[j]
                if el2 > el and el2 <= N:
                    indices[j] -= 1
        else:
            move_item(arr, el, N)
            for j in range(len(indices)):
                el2 = indices[j]
                if el2 > N and el2 < el:
                    indices[j] += 1

        #we have inserted an elem after N
        #next target is our last elem
        N += 1
        print('now', arr, 'indices', indices)


move_items([0,1,2,3,4,5,6,7,8,9,10,11,12], [8,2,7,4,0],6)    #[1, 3, 5, 8, 2, 7, 4, 0, 6, 9, 10, 11, 12]
move_items([0,1,2,3,4,5,6,7,8,9,10,11,12], [8,2,7,4,0,10],6) #[1, 3, 5, 8, 2, 7, 4, 0, 10, 6, 9, 11, 12]
move_items([0,1,2,3,4,5,6,7,8,9,10,11,12], [1,10,5,7,3,8],6) #[0, 2, 3, 4, 1, 10, 5, 6, 7, 8, 9, 11, 12]
def moveitems(from_indices, to_index):

    while from_indices:
        el = from_indices.pop(0)
        if el != to_index:
            if el < to_index: to_index -=1

            moveitem(el,to_index)
            for i in range(len(from_indices)):
                el2 = from_indices[i]
                if (el < to_index) and (el2 > el) and (el2 <= to_index):
                    from_indices[i] -= 1
                elif (el > to_index) and (el2 > to_index) and (el2 < el):
                    from_indices[i] += 1

        to_index += 1