如何解决就地将排列应用于列表的方法? 按键排序的倒数
| 这是我想做的一个例子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\",\"Ni\",\"knights\"]
我可以用enumerate
,list
等来做,但我想直接影响spam_list
,例如list.sort()
,而不复制像sorted()
编辑:推送一个字符串示例,以避免索引和and3ѭ的值之间的混淆
编辑:原来这是重复的Python排序并行数组吗?好吧,我不能为SO一致性参数付出太多努力。
解决方法
您可以尝试:
spam_list = [spam_list[i] for i in spam_order]
,您可以给sort函数一个特殊的key
:
order = dict(zip(spam_list,spam_order))
spam_list.sort(key=order.get)
编辑:正如@ninjagecko在他的答案中指出的那样,这并不是真正有效的方法,因为它会复制两个列表以创建用于查找的字典。但是,使用OP给出的修改示例,这是唯一的方法,因为必须建立一些索引。好处是,至少对于字符串而言,不会复制这些值,因此开销只是字典本身的开销。
, 但我想直接影响spam_list,例如list.sort(),而不是像sorted()那样复制它
只有一个解决方案可以满足您的要求。每个其他解决方案都隐式地复制一个或两个列表(或将其转换为字典等)。您需要的是一种方法,该方法在原位对两个列表进行排序,使用的额外空间为[10],其中一个列表为另一个列表的键。我个人只是接受额外的空间复杂性,但是如果您确实想要,可以这样做:
(编辑:原始海报可能并不真正在乎ѭ11,因为它很有效,而是因为它修改了状态;总的来说,这是一种危险的事情,并且是非低级语言尝试避免甚至禁止它,但是使用切片分配的解决方案将实现“就地”语义)
创建一个自定义词典子类(实际上是Zip
类),该子类由要排序的两个列表支持。
索引myZip[i]
->得到元组(list1[i],list2[i])
分配myZip[i]=(x1,x2)
->分派到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)
不幸的是,没有办法只对10个空格中的一个列表进行排序而不对另一个列表进行排序。如果您不想同时对两个列表进行排序,则不妨采用构建虚拟列表的原始方法。
但是,您可以执行以下操作:
spam_list.sort(key=lambda x:x)
但是,如果key或cmp函数对任何集合进行了任何引用(例如,如果传递了dict.__getitem__
的字典,则必须构造),这并不比原始的O(N)
空间方法好,除非您已经碰巧拥有这样的字典躺在周围。
原来这是重复出现的Python排序并行数组问题? ,但是除了这个问题之外,该问题也没有正确的答案,该答案与我的等效,但没有示例代码。除非您进行了令人难以置信的优化或专用代码,否则我只会使用您的原始解决方案,该解决方案的空间复杂度与其他解决方案相同。
编辑2:
正如senderle所指出的,OP根本不需要排序,而是希望应用排列。为此,您可以并且应该使用简单的索引编制方式,使其他答案显示为[spam_list[i] for i in spam_order]
,但是仍必须进行显式或隐式复制,因为您仍然需要中间数据。 (与记录无关,为了进行记录,应用逆排列是我认为与标识并行排序的逆,虽然排序时间效率较低,但您可以使用一个获取另一个,这是.28ѭ,然后按by29ѭ排序。我离开上面有关整理记录的讨论。)
编辑3:
但是,有可能在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)个地方运行,而不使用Bloom过滤器;如果使用它们,则在〜O(N)个时间和O(#cycle + bloomfilter_space)空间中运行
,如果问题是特定位置的问题,而不是内存使用本身的问题(换句话说,如果您希望这样做有副作用),则可以使用切片分配。从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的出色答案-生成器切片分配确实会生成一个隐式元组。这意味着它很安全,但内存效率却不如您想象的那样。不过,元组比列表更有效地利用内存,因此从该角度来看,生成器表达式是更可取的。
,map(lambda x:spam_list[x],spam_order)
,如果您实际上根本不关心效率,而只想要就地语义(这有点奇怪,因为有专门用于避免就地语义的整个编程语言),那么您可以这样做:
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\']
归功于senderle,因为他们意识到这实际上是OP的追求。他应该随时将此答案复制到自己的答案中。除非您真的更喜欢他,否则不应该接受这个答案。
,您可以使用numpy。
import numpy as np
spam_list = list(np.array(spam_list)[spam_order])
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。