给定一个未排序的python列表,我如何找到排序所需的最小移动集

时间:2014-01-28 15:31:37

标签: python sorting

我有一个存储在远程数据库中的项目列表,这些项目可能是未排序的,我想对它们进行排序。数据库接受以下命令:

move item1 before item2
move item3 after item2

所以,给出一个表格列表:

[1,3,2,7,6,0,4]

......我怎样才能得到一系列动作:

move 2 before 3
move 7 after 6
move 0 before 1
move 4 before 6

我假设对bubblesort算法的修改可行,但我特别寻找仍然是pythonic的最有效的实现,并且生成最少的移动命令。

更新:列表长度为1000-10000,所有项目都是唯一的 - 没有重复。只有极少数项目 - 1-10 - 在任何给定时间都会出错。时间是一个值得关注的问题 - 它需要几秒钟,而不是几分钟 - 但它不必非常非常

更新2:我还想只移动一次项目

3 个答案:

答案 0 :(得分:3)

由于你想减少移动序列的数量,我能想到的最佳方法是在排序列表上使用二进制搜索来确定每个元素的插入点。如果任何元素已经处于正确位置,则无需移动它。

这将生成n - d个序列移动,其中n是元素数量,d是元素数量正确的位置。

  • 对于已排序的列表,序列移动的数量为n - d = n - n = 0
  • 对于所有元素位置错误的列表,序列移动的数量为n - d = n - 0 = n

<强>实施

def gen_move(seq):
    from bisect import bisect_left
    out = seq[0:1]
    for elem in seq[1:]:
        index = bisect_left(out, elem)
        if seq[index] != elem:
            if index == 0:
                print "Move {} before {}".format(elem, out[index])
            else:
                print "Move {} after {}".format(elem, out[index - 1])
        out.insert(index, elem)
    print out

<强>演示

gen_move([1,3,2,7,6,0,4])
Move 2 after 1
Move 6 after 3
Move 0 before 1
Move 4 after 3
[0, 1, 2, 3, 4, 6, 7]

gen_move(range(10)[::-1])
Move 8 before 9
Move 7 before 8
Move 6 before 7
Move 5 before 6
Move 4 before 5
Move 3 before 4
Move 2 before 3
Move 1 before 2
Move 0 before 1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

gen_move(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

<强>性能

In [5]: %timeit gen_move(range(10000, 0, -1))
10000 loops, best of 3: 84 us per loop

时间复杂度

sum(1 ln 1 + 2 ln 2 + 3 ln 3 + ..... n ln n) < O(n ln n)

空间复杂性

O(n)

答案 1 :(得分:2)

  1. 从远程数据库获取数据
  2. 对它们进行排序(只是简单的sort
  3. 使用difflib SequenceMatcher.get_opcodes获取将原始列表转换为已排序列表的替换/删除/插入/跳过操作
  4. 将这些操作转换为“在Y之后/之前移动X”操作
  5. 我有点担心difflib的时间复杂性,所以你应该根据预期的数据大小进行基准测试。更快的替代方案可能是滚动校验和算法(如librsync)。

答案 2 :(得分:1)

这很天真,但似乎有效:

xs = [1,3,2,7,6,0,4]
ys = []

for x in xs:
    for n, y in enumerate(ys):
        if x < y:
            print x, 'before', y
            ys.insert(n, x)
            break
    else:
        ys.append(x)

结果:

2 before 3
6 before 7
0 before 1
4 before 6