具有QuickSort风格的QuickSelect算法

时间:2018-05-23 10:43:03

标签: python algorithm quickselect

我试图编写最佳算法来选择列表中较大的第i个元素。 例如,如果array = [4,3,5,7]并且我搜索第二个,则函数将返回4.

我假设列表中只有不同的数字

问题在于:

该功能有时会返回无。

这是我的代码(我认为第一个函数效果很好)。

from random import shuffle

def partition(array, leftend, rightend, pivot):
    """ I tested this one and it should work fine """
    i = leftend
    pivotindex = array.index(pivot)  # only works if all values in array unique
    array[pivotindex] = array[leftend]
    array[leftend] = pivot
    for j in range(leftend+1, rightend):
        if array[j] < pivot:
            temp = array[j]
            array[j] = array[i]
            array[i] = temp
            i += 1
    pivotindex = array.index(pivot)  # only works if all values in array unique
    leftendval = array[pivotindex]   # Take the value of the pivot
    array[pivotindex] = array[i]
    array[i] = leftendval
    return array

def RSelect(array, n, statistic_order):
    """ list * int * int
        statistic_order = the i th element i'm searching for """
    new_array = []                  # is used at the end of the function
    if n == 1:
        return array[0]
    array_temp = array              # Allows to have a shuffled list and
    shuffle(array_temp)
    pivot = array_temp[0]           # Allows to have a random pivot
    partition(array,0,n,pivot)
    j = array.index(pivot)


    if j == statistic_order:
        return pivot


    elif j > statistic_order:
        for k in range(0,j):
            new_array.append(array[k])
        RSelect(new_array,j,statistic_order)


    elif j < statistic_order:
        for k in range(j+1,n):
            new_array.append(array[k])
        RSelect(new_array,(n-j)-1,statistic_order-j)

2 个答案:

答案 0 :(得分:0)

有些事情是错的:

  • 在每种情况下,您都需要以递归方法返回结果!
  • 当j < statistic_order,你递归地处理数组的右边部分,你丢弃j + 1个数字,而不是j。记住索引在python中从0开始,而不是1!

我还改变了一些事情,比如无用的参数,或者用可以用切片写的循环。

这是最终的代码,检查更改以确保您理解它。

RSelect:

def RSelect(array, statistic_order):
    """ list * int * int
    statistic_order = the i th element i'm searching for """
    n = len(array)
    if n == 1:
        return array[0]
    array_temp = array              # Allows to have a shuffled list and
    shuffle(array_temp)
    pivot = array_temp[0]           # Allows to have a random pivot
    array = partition(array,0,n,pivot)  # Changes here, does not impact the result, but for readability
    j = array.index(pivot)

    # print(array, j, statistic_order, end = '\t')
    if j == statistic_order:
        return pivot

    elif j > statistic_order:
        assert j > 0
        # print((array[0:j]), pivot)
        return RSelect(array[0:j],statistic_order)  # Changes here : return

    elif j < statistic_order:
        assert j+1 < n
        # print(pivot, (array[j+1:n]))
        return RSelect(array[j+1:n],statistic_order-j-1)  # Changes here : return, -j-1

主要:

if __name__ == "__main__":
    from sys import argv
    if len(argv) > 1:
       n = int(argv[1])
    arr = [2, 1, 3, 5, 4]
    print(RSelect(arr[:], n))

为此目的,O(n)中还存在另一种算法:见this

编辑:拼写错误&amp; amp;关于复杂性的纠正

答案 1 :(得分:0)

代码工作正常但仍然是,结果从0开始。 例如,如果arr = [2,3,5,6]并且我要求RSelect(arr,4,2),答案将是5而不是3.我不知道为什么。

以下是更新的代码:

from random import shuffle

def partition(array, leftend, rightend, pivot):
    i = leftend
    pivotindex = array.index(pivot)  # only works if all values in array unique
    array[pivotindex] = array[leftend]
    array[leftend] = pivot
    for j in range(leftend+1, rightend):
        if array[j] < pivot:
            temp = array[j]
            array[j] = array[i]
            array[i] = temp
            i += 1
    pivotindex = array.index(pivot)  # only works if all values in array unique
    leftendval = array[pivotindex]   # Take the value of the pivot
    array[pivotindex] = array[i]
    array[i] = leftendval


def RSelect(array, n, statistic_order):
    """ list * int * int
        statistic_order = the i th element i'm searching for """
    if n == 1:
        return array[0]

    array_temp = array              # Allows to have a shuffled list
    shuffle(array_temp)
    pivot = array_temp[0]           # Allows to have a random pivot
    partition(array,0,n,pivot)
    j = array.index(pivot)


    if j == statistic_order:
        return pivot


    elif j > statistic_order:
        return RSelect(array[0:j],j,statistic_order)


    elif j < statistic_order:
        return RSelect(array[j+1:n],(n-j)-1,statistic_order-j-1)