" Unsorting"一个快捷方式

时间:2018-01-16 23:28:42

标签: python algorithm sorting quicksort

(快速注意!虽然我知道在Python中有很多选项可供排序,但这段代码更像是一个概括的概念验证,后来会被移植到另一种语言中,所以我赢了#39;能够使用任何特定的Python库或函数。

此外,您提供的解决方案并非必须遵循我的方法。)

背景

我有一个快速排序算法,并且我正在尝试实施一种方法,以便以后能够提供非常规的'已排序元素的新位置。也就是说,如果元素A位于索引x并且被排序为索引y,那么'指针' (或者,取决于您的术语,引用或映射)数组将其值x的值从x更改为y

更详细:
您使用数组arr开始程序,并使用一组给定的数字。此数组稍后将通过快速排序算法运行,因为对数组进行排序对于将来对其进行处理非常重要。

这个数组的排序很重要。因此,您有另一个数组ref,其中包含原始数组的索引,以便在将引用数组映射到数组时,将再现数组的原始顺序。

在对数组进行排序之前,数组和映射如下所示:

arr = [1.2, 1.5, 1.5, 1.0, 1.1, 1.8]
ref = [0,   1,   2,   3,   4,   5]
--------
map(arr,ref) -> [1.2, 1.5, 1.5, 1.0, 1.1, 1.8]

您可以看到ref的索引0指向arr的索引0,为您提供1.2ref的索引1指向arr的索引1,为您提供1.5,依此类推。

对算法进行排序时,应重新排列ref,以便在按照上述步骤进行映射时,会生成预先排序的arr

arr = [1.0, 1.1, 1.2, 1.5, 1.5, 1.8]
ref = [2,   3,   4,   0,   1,   5]
--------
map(arr,ref) -> [1.2, 1.5, 1.5, 1.0, 1.1, 1.8]

同样,ref的索引0是2,因此映射数组的第一个元素是arr[2]=1.2ref的索引1是3,因此映射数组的第二个元素是arr[3]=1.5,依此类推。

问题

我的代码的当前实现非常适合排序,但可怕用于重新映射ref

给定相同的数组arr,我的程序输出如下:

arr = [1.0, 1.1, 1.2, 1.5, 1.5, 1.8]
ref = [3,   4,   0,   1,   2,   5]
--------
map(arr,ref) -> [1.5, 1.5, 1.0, 1.1, 1.2, 1.8]

这是一个问题,因为这个映射肯定等于原始:

[1.5, 1.5, 1.0, 1.1, 1.2, 1.8] != [1.2, 1.5, 1.5, 1.0, 1.1, 1.8]

我的方法是:

  1. a的{​​{1}}和b中的xy中的元素切换时,
  2. 然后设置arrref[x] = y
  3. 这不起作用,我无法想到另一个不需要ref[y] = x时间的解决方案。

    谢谢!

    最小可重复的示例

    O(n^2)

5 个答案:

答案 0 :(得分:3)

可以做你所要求的,但是当我们在现实生活中必须做这样的事情时,我们通常会混淆sort的比较函数而不是swap函数。使用通用语言提供的排序例程通常具有内置的功能,因此您无需编写自己的排序。

在此过程中,您将ref数组(下面称为order)按其指向的arr值的值进行排序。生成相同的ref数组,但没有修改arr

使用此排序进行映射可对原始数组进行排序。您希望它 unsort 排序的数组,这就是您的代码无法正常工作的原因。

您可以反转此顺序以获取您最初查找的ref数组,或者只需将arr保留为未排序,并在需要时将其映射到order。< / p>

arr = [1.5, 1.2, 1.0, 1.0, 1.2, 1.2, 1.5, 1.3, 2.0, 0.7, 0.2, 1.4, 1.2, 1.8, 2.0, 2.1]

order = range(len(arr))
order.sort(key=lambda i:arr[i])

new_arr = [arr[order[i]] for i in range(len(arr))]

print("original array = {}".format(arr))
print("sorted ordering = {}".format(order))
print("sorted array = {}".format(new_arr))

ref = [0]*len(order)
for i in range(len(order)):
    ref[order[i]]=i

unsorted = [new_arr[ref[i]] for i in range(len(ref))]
print("unsorted after sorting = {}".format(unsorted))

输出:

original array = [1.5, 1.2, 1.0, 1.0, 1.2, 1.2, 1.5, 1.3, 2.0, 0.7, 0.2, 1.4, 1.2, 1.8, 2.0, 2.1]
sorted ordering = [10, 9, 2, 3, 1, 4, 5, 12, 7, 11, 0, 6, 13, 8, 14, 15]
sorted array = [0.2, 0.7, 1.0, 1.0, 1.2, 1.2, 1.2, 1.2, 1.3, 1.4, 1.5, 1.5, 1.8, 2.0, 2.0, 2.1]
unsorted after sorting = [1.5, 1.2, 1.0, 1.0, 1.2, 1.2, 1.5, 1.3, 2.0, 0.7, 0.2, 1.4, 1.2, 1.8, 2.0, 2.1]

答案 1 :(得分:1)

这并不是那么可怕:你只是改变了你的参考用法。您的索引ref告诉您如何从原始列表构建排序列表。但是,您已经在相反的方向上使用它:您已将其应用于排序列表,尝试重建原始图像。你需要逆映射。

这足以让你解决问题吗?

答案 2 :(得分:1)

我认为你可以在事后修复你的ref数组。在您的代码示例中,只需在调用quick_sort(a,b)

后插入以下代码段即可
c = range(1, len(b)+1)
for i in range(0, len(b)):
    c[ b[i]-1 ] = i+1

c数组现在应该包含正确的引用。

答案 3 :(得分:1)

窃取/重写@Prune所写的内容:b中的内容是正向转换,即排序本身。将其应用于a0可提供已排序的列表(print_links(a0,b)
你只需通过查找哪个元素到达什么位置来恢复它:

c=[b.index(i)+1 for i in range(1,len(a)+1)]
print_links(a,c)

答案 4 :(得分:1)

您不需要维护索引和元素的映射,只需在对数组进行排序时对索引进行排序。例如:

unsortedArray =  [1.2, 1.5, 2.1]
unsortedIndexes = [0,   1,   2]
sortedAray = [1.2, 1.5, 2.1]

然后您在排序0 and 1时只需交换unsortedArray。并获取sortedIndexes [1, 0, 2],您可以按sortedArray[1],sortedArray[0],sortedArray[2]获取原始数组。

def inplace_quick_sort(s, indexes, start, end):
    if start>= end:
        return
    pivot = getPivot(s, start, end)#it's should be a func
    left = start
    right = end - 1
    while left <= right:
        while left <= right and customCmp(pivot, s[left]):
        # s[left] < pivot:
            left += 1
        while left <= right and customCmp(s[right], pivot):
        # pivot < s[right]:
            right -= 1
        if left <= right:
            s[left], s[right] = s[right], s[left]
            indexes[left], indexes[right] = indexes[right], indexes[left]
            left, right = left + 1, right -1
    s[left], s[end] = s[end], s[left]
    indexes[left], indexes[end] = indexes[end], indexes[left]
    inplace_quick_sort(s, indexes, start, left-1)
    inplace_quick_sort(s, indexes, left+1, end)
def customCmp(a, b):
        return a > b
def getPivot(s, start, end):
    return s[end]
if __name__ == '__main__':
    arr = [1.5,1.2,1.0,1.0,1.2,1.2,1.5,1.3,2.0,0.7,0.2,1.4,1.2,1.8,2.0,2.1]
    indexes = [i for i in range(len(arr))]
    inplace_quick_sort(arr,indexes, 0, len(arr)-1)
    print("sorted = {}".format(arr))
    ref = [0]*len(indexes)
    for i in range(len(indexes)):
        #the core point of Matt Timmermans' answer about how to construct the ref
        #the value of indexes[i] is index of the orignal array
        #and i is the index of the sorted array,
        #so we get the map by ref[indexes[i]] = i
        ref[indexes[i]] = i
    unsorted = [arr[ref[i]] for i in range(len(ref))]
    print("unsorted after sorting = {}".format(unsorted))