以下是我想要做的一个例子
spam_list = ["We", "are", "the", "knights", "who", "say", "Ni"]
spam_order = [0,1,2,4,5,6,3]
spam_list.magical_sort(spam_order)
print(spam_list)
["We", "are", "the", "who", "say", "Ni", "knights"]
我可以使用enumerate
,list
等进行此操作,但我希望直接影响spam_list
,例如list.sort()
,而不是像{{1}那样复制它}}
编辑:推送了一个字符串示例,以避免在sorted()
修改:结果证明这是Python sort parallel arrays in place?的副本。好吧,我不能删除SO一致性论点的那么多努力。
答案 0 :(得分:21)
你可以尝试:
spam_list = [spam_list[i] for i in spam_order]
答案 1 :(得分:11)
您可以为排序功能提供特殊的key
:
order = dict(zip(spam_list, spam_order))
spam_list.sort(key=order.get)
编辑:正如@ninjagecko在his answer中指出的那样,这并不是很有效,因为它会复制两个列表以创建查找字典。但是,通过OP给出的修改示例,这是唯一的方法,因为必须构建一些索引。好处是,至少对于字符串,值不会被复制,因此开销只是字典本身的开销。
答案 2 :(得分:5)
但是我想直接影响spam_list,比如list.sort()而不是像sorted()
那样复制它
只有 ONE 解决方案,这完全符合您的要求。 每个其他解决方案都隐含地制作一个或两个列表的副本(或将其转换为dict等)。您要求的是一种对两个列表进行排序的方法,< strong>使用O(1)
额外空格,使用一个列表作为另一个列表的键。我个人会接受额外的空间复杂性,但如果你真的想要,你可以这样做:
(编辑:可能是原始海报并不真正关心.sort
,因为它有效,而是因为它改变了状态;一般来说,这是一个危险的想要和非低级别语言试图避免这种情况甚至禁止它,但使用切片赋值的解决方案将实现“就地”语义)
Zip
类),它由您正在排序的两个列表支持。myZip[i]
- &gt;结果是元组(list1[i],list2[i])
myZip[i]=(x1,x2)
- &gt;发送到list1[i]=x1, list2[i]=x2
。myZip(spam_list,spam_order).sort()
,现在spam_list
和spam_order
就地排序示例:
#!/usr/bin/python3
class LiveZip(list):
def __init__(self, list1, list2):
self.list1 = list1
self.list2 = list2
def __len__(self):
return len(self.list1)
def __getitem__(self, i):
return (self.list1[i], self.list2[i])
def __setitem__(self, i, tuple):
x1,x2 = tuple
self.list1[i] = x1
self.list2[i] = x2
spam_list = ["We", "are", "the", "knights", "who", "say", "Ni"]
spam_order = [0,1,2,4,5,6,3]
#spam_list.magical_sort(spam_order)
proxy = LiveZip(spam_order, spam_list)
现在让我们看看它是否有效......
#proxy.sort()
#fail --> oops, the internal implementation is not meant to be subclassed! lame
# It turns out that the python [].sort method does NOT work without passing in
# a list to the constructor (i.e. the internal implementation does not use the
# public interface), so you HAVE to implement your own sort if you want to not
# use any extra space. This kind of dumb. But the approach above means you can
# just use any standard textbook in-place sorting algorithm:
def myInPlaceSort(x):
# [replace with in-place textbook sorting algorithm]
现在有效:
myInPlaceSort(proxy)
print(spam_list)
不幸的是无法在O(1)
空间中对一个列表进行排序,而无需对其他列表进行排序;如果你不想对两个列表进行排序,你可以采用构建虚拟列表的原始方法。
但您可以执行以下操作:
spam_list.sort(key=lambda x:x)
但是如果key或cmp函数对任何集合进行任何引用(例如,如果你传入一个必须构建的dict的dict.__getitem__
),这并不比你原来的O(N)
空间好。方法,除非你已经碰巧有这样的字典。
原来这是Python sort parallel arrays in place?的重复问题,但该问题除了this one之外也没有正确答案,这与我的相同,但没有示例代码。除非你是非常优化或专门的代码,否则我只会使用你原来的解决方案,这在空间复杂性方面与其他解决方案相当。
EDIT2:
正如发送者指出的那样,OP根本不想要排序,而是希望,我认为,应用排列。为了实现这一点,您可以并且应该使用其他答案建议[spam_list[i] for i in spam_order]
的简单索引,但必须保持显式或隐式副本,因为您仍然需要中间数据。 (不相关和记录,应用逆置换是我认为与身份并行排序的逆,你可以使用一个来获得另一个,虽然排序时间效率较低。_,spam_order_inverse = parallelSort(spam_order, range(N))
,然后按spam_order_inverse
排序。我将上述关于为记录排序的讨论留下。)
EDIT3:
但是,有可能在O(#cycles)
空间内实现就地排列,但时间效率却很高。每个排列都可以分解为在子集上并行应用的不相交排列。这些子集称为循环或轨道。期间等于它们的大小。你这样做了一个信念的飞跃,并做如下:
Create a temp variable.
For index i=0...N:
Put x_i into temp, assign NULL to x_i
Swap temp with x_p(i)
Swap temp with x_p(p(i))
...
Swap temp with x_p(..p(i)..), which is x_i
Put a "do not repeat" marker on the smallest element you visited larger than i
Whenever you encounter a "do not repeat" marker, perform the loop again but
without swapping, moving the marker to the smallest element larger than i
To avoid having to perform the loop again, use a bloom filter
这将在O(N ^ 2)时间和O(#cycles)地方运行,没有布隆过滤器,或者O(N)时间和O(#cycle + bloomfilter_space)空间如果你使用它们
答案 3 :(得分:3)
如果问题特别是 in-placeness 而不是内存使用本身 - 如果你想让它有副作用,换句话说 - 那么你可以使用切片分配。从Peter Collingridge窃取:
other_spam_list = spam_list
spam_list[:] = [spam_list[i] for i in spam_order]
assert other_spam_list == spam_list
看起来你甚至可以用生成器表达式做到这一点!但我怀疑这仍然隐含地创建了某种新的序列 - 可能是一个元组。如果没有,我认为它会表现出错误的行为;但我测试了它,它的行为似乎是正确的。
spam_list[:] = (spam_list[i] for i in spam_order)
啊哈!通过无法模仿的Sven Marnach - 生成器切片分配确实生成一个隐式元组。这意味着它是安全的,但不像你想象的那样具有内存效率。尽管如此,元组比列表更有内存效率,因此从这个角度来看,生成器表达式更为可取。
答案 4 :(得分:2)
map(lambda x:spam_list[x], spam_order)
答案 5 :(得分:2)
如果你真的根本不关心效率,只想要就地语义(这有点奇怪,因为有完整的编程语言致力于避免就地语义),那么你可以这样做:
def modifyList(toModify, newList):
toModify[:] = newList
def permuteAndUpdate(toPermute, permutation):
modifyList(toPermute, [toPermute[i] for i in permutation])
permuteAndUpdate(spam_list, spam_order)
print(spam_list)
# ['We', 'are', 'the', 'Ni', 'knights', 'who', 'say']
感谢发送者认识到这可能是OP之后实际存在的内容;他应该随意将这个答案复制到他自己的答案中。除非你真的更喜欢这个答案,否则不应该接受这个答案。
答案 6 :(得分:0)
您可以使用numpy。
import numpy as np
spam_list = list(np.array(spam_list)[spam_order])