我写了一个快速选择算法如下:
def partition(arr):
pvt = 0
for i in range(len(arr) - 1):
if arr[i] < arr[-1]:
arr[i], arr[pvt] = arr[pvt], arr[i] # mv smaller elements before pvt
pvt += 1
arr[-1], arr[pvt] = arr[pvt], arr[-1]
return pvt
def quickselect(k, arr):
p = partition(arr)
while k != p:
p = partition(arr[:p]) if k < p else partition(arr[p:])
return arr[k]
我打算做的是在具有O(n)复杂度的arr中找到第k个元素。但是当我完成编码时,我发现arr [:p]实际上传递的是副本而不是原始列表的一部分,这不会使递归分区正常工作。据我所知,熊猫系列切片实际上做了我想要的。如果我将arr作为pd.Series传递,它似乎正常工作。但是有更原生的方式来传递python list arr [:p]的原始片段吗?
答案 0 :(得分:5)
arr[:p]
是副本,因此无法在不复制的情况下传递它。
正如您已经发现的那样,您可以使用“查看切片”的其他类型 - memoryview
,np.array
,pd.Series
等。
或者您可以将整个列表和切片信息一起传递,如下所示:
def partition(arr, start, stop):
pvt = 0
for i in range(start, stop - 1):
if arr[i] < arr[stop-1]:
arr[i], arr[pvt] = arr[pvt], arr[i] # mv smaller elements before pvt
pvt += 1
arr[stop-1], arr[pvt] = arr[pvt], arr[stop-1]
return pvt
def quickselect(k, arr, start=None, stop=None):
if start is None: start = 0
if stop is None: stop = len(arr)
p = partition(arr, start, stop)
while k != p:
p = partition(arr, start, p) if k < p else partition(arr, p, stop)
return arr[k]
或者您可以编写一个简单的包装器,它包含列表,启动和停止值,并通过适当的转发实现collections.abc.Sequence
(或MutableSequence
)。这是更多的工作,但它可能会使你编写的其他代码更具可读性。
您可以找到可变且不可变的切片视图的简单版本here。它没有像我希望的那样经过彻底的测试,但它似乎适用于你的quickselect
。 (当然,线性而非二次工作的成本可能会被为每个切片构建一个包装器对象的常数乘数所淹没,直到你得到合适大小的列表......)
答案 1 :(得分:0)
一种可能的解决方案是使用numpy或pandas以及abarnert先生所说的。但是导入包并不总是适用于纯粹的算法问题。
import numpy as np
def partition(arr):
pvt = 0
for i in range(len(arr) - 1):
if arr[i] < arr[-1]:
arr[i], arr[pvt] = arr[pvt], arr[i] # mv smaller elements before pvt
pvt += 1
arr[-1], arr[pvt] = arr[pvt], arr[-1]
return pvt
def quickselect(k, arr):
arr = np.array(arr)
p = partition(arr)
while k != p:
p = partition(arr[:p]) if k < p else partition(arr[p:])
return arr[k]