Algorithm 将数组的元素分成3组

Algorithm 将数组的元素分成3组,algorithm,computer-science,median-of-medians,Algorithm,Computer Science,Median Of Medians,我必须把数组的元素分成3组。这需要在不排序数组的情况下完成。考虑例子 我们有120个未排序的值,因此最小的40个值需要在第一组中,接下来的40个值需要在第二组中,最大的40个值需要在第三组中 我在考虑中值中值法,但无法将其应用于我的问题,请建议一种算法。您可以在阵列上调用两次,以便在适当的位置和平均情况下以线性时间执行此操作。通过使用线性时间为quickselect选择最佳枢轴,最坏情况下的运行时也可以改进为O(n) 对于quickselect的两个调用,使用k=n/3。第一次调用时,在整个阵列

我必须把数组的元素分成3组。这需要在不排序数组的情况下完成。考虑例子

我们有120个未排序的值,因此最小的40个值需要在第一组中,接下来的40个值需要在第二组中,最大的40个值需要在第三组中

我在考虑中值中值法,但无法将其应用于我的问题,请建议一种算法。

您可以在阵列上调用两次,以便在适当的位置和平均情况下以线性时间执行此操作。通过使用线性时间为quickselect选择最佳枢轴,最坏情况下的运行时也可以改进为O(n)

对于quickselect的两个调用,使用k=n/3。第一次调用时,在整个阵列上使用quickselect,第二次调用时,从arr[k..n-1](对于0索引阵列)使用quickselect

维基百科对quickselect的解释:

k = n / 3

# First group smallest elements in array
quickselect(L, 0, n - 1, k) # Call quickselect on your entire array

# Then group middle elements in array
quickselect(L, k, n - 1, k) # Call quickselect on subarray

# Largest elements in array are already grouped so
# there is no need to call quickselect again
Quickselect使用与快速排序相同的总体方法,即选择一种 元素作为轴心,并基于 枢轴,因此小于或大于枢轴。然而, quickselect不是像在快速排序中那样递归到两边,而是 只会递归到一个面–元素所在的面 寻找。这降低了O(n logn)(in)的平均复杂度 快速排序)到O(n)(在快速选择中)

与快速排序一样,quickselect通常作为就地 算法,并且除了选择第k个元素之外,它还部分 对数据进行排序。请参阅,以了解有关 与分拣的联系

要将数组元素分为3组,请使用以下用Python编写的算法并结合quickselect:

k = n / 3

# First group smallest elements in array
quickselect(L, 0, n - 1, k) # Call quickselect on your entire array

# Then group middle elements in array
quickselect(L, k, n - 1, k) # Call quickselect on subarray

# Largest elements in array are already grouped so
# there is no need to call quickselect again
所有这些的关键点是quickselect使用一个名为partition的子例程。分区将数组分为两部分,一部分大于给定元素,另一部分小于给定元素。因此,它将围绕该元素对数组进行部分排序,并返回其新的排序位置。因此,通过使用quickselect,可以在适当的位置和平均线性时间内,围绕第k个元素对数组进行部分排序(请注意,这与实际对整个数组进行排序不同)

quickselect的时间复杂性:

k = n / 3

# First group smallest elements in array
quickselect(L, 0, n - 1, k) # Call quickselect on your entire array

# Then group middle elements in array
quickselect(L, k, n - 1, k) # Call quickselect on subarray

# Largest elements in array are already grouped so
# there is no need to call quickselect again
  • 最坏情况性能O(n2)
  • 最佳案例性能O(n)
  • 平均案例绩效O(n)
  • quickselect的运行时几乎总是线性的,而不是二次的,但这取决于这样一个事实,即对于大多数数组,简单地选择一个随机轴心点几乎总是产生线性运行时。但是,如果要提高quickselect的最差性能,可以选择在每次调用之前使用,以接近quickselect要使用的最佳轴心。这样,您将把quickselect算法的最坏情况性能提高到O(n)。这种开销可能不是必需的,但如果您处理的是大量随机整数列表,它可以防止算法出现异常的二次运行时

    下面是一个完整的Python示例,它实现了quickselect,并将其应用于120个整数的反向排序列表两次,并打印出三个子列表

    from random import randint
    
    
    def partition(L, left, right, pivotIndex):
        '''partition L so it's ordered around L[pivotIndex]
           also return its new sorted position in array'''
        pivotValue = L[pivotIndex]
        L[pivotIndex], L[right] = L[right], L[pivotIndex]
        storeIndex = left
        for i in xrange(left, right):
            if L[i] < pivotValue:
                L[storeIndex], L[i] = L[i], L[storeIndex]
                storeIndex = storeIndex + 1
        L[right], L[storeIndex] = L[storeIndex], L[right]
        return storeIndex
    
    
    def quickselect(L, left, right, k):
        '''retrieve kth smallest element of L[left..right] inclusive
           additionally partition L so that it's ordered around kth 
           smallest element'''
        if left == right:
            return L[left]
        # Randomly choose pivot and partition around it
        pivotIndex = randint(left, right)
        pivotNewIndex = partition(L, left, right, pivotIndex)
        pivotDist = pivotNewIndex - left + 1
        if pivotDist == k:
            return L[pivotNewIndex]
        elif k < pivotDist:
            return quickselect(L, left, pivotNewIndex - 1, k)
        else:
            return quickselect(L, pivotNewIndex + 1, right, k - pivotDist)
    
    
    def main():
        # Setup array of 120 elements [120..1]
        n = 120
        L = range(n, 0, -1)
    
        k = n / 3
    
        # First group smallest elements in array
        quickselect(L, 0, n - 1, k) # Call quickselect on your entire array
    
        # Then group middle elements in array
        quickselect(L, k, n - 1, k) # Call quickselect on subarray
    
        # Largest elements in array are already grouped so 
        # there is no need to call quickselect again
    
        print L[:k], '\n'
        print L[k:k*2], '\n'
        print L[k*2:]
    
    
    if __name__ == '__main__':
        main()
    
    来自随机导入randint
    def分区(左、左、右、数据透视索引):
    ''分区L,所以它是围绕L[数据透视索引]排序的
    还返回其在数组“”中的新排序位置
    pivotValue=L[数据透视索引]
    L[pivotIndex],L[right]=L[right],L[pivotIndex]
    storeIndex=左
    对于X范围内的i(左、右):
    如果L[i]
    我想看看。统计样本的第k阶统计量等于其第k个最小值。计算列表中第k个最小(或最大)元素的问题称为选择问题,由一个简单的方法解决

    中位数的中间值是正确的。但是,您可能希望从数组中查找最小的第20个和第40个元素,而不是查找中值。就像找到中值一样,使用选择算法只需线性时间就可以找到两者。最后,遍历数组并根据这两个元素对元素进行分区,这也是线性时间


    注:如果这是您在算法类中的练习,可能会帮助您:)

    分配一个与输入数组大小相同的数组 扫描输入数组一次,并跟踪数组的最小值和最大值。 同时将第二个数组的所有值设置为1。 计算
    delta=(最大-最小)/3
    。 再次扫描阵列,如果数字为
    >min+d,则将第二个阵列设置为2