将数组的元素分为3组

时间:2013-10-11 20:48:51

标签: algorithm computer-science median-of-medians

我必须将数组的元素分成3组。这需要在不对数组进行排序的情况下完成。考虑一下这个例子

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

我在考虑中位数方法的中位数,但无法将其应用于我的问题,请提出算法。

3 个答案:

答案 0 :(得分:6)

您可以在阵列上两次调用quickselect来就地执行此操作,并在平均情况下线性时间。通过使用线性时间median of medians algorithm来选择最佳枢轴以进行快速选择,最坏情况运行时也可以改进为O(n)。

对于两次快速选择的调用,使用k = n / 3.在第一次调用时,在整个数组上使用quickselect,在第二次调用时,从arr [k..n-1]使用它(对于0 -indexed array)。

维基百科对quickselect的解释:

  

Quickselect使用与quicksort相同的整体方法,选择一个   element作为一个数据透视表,并根据数据将数据分成两部分   因此,枢轴小于或大于枢轴。然而,   而不是递归到双方,如快速排序,快速选择   只有一个方面才能进入 - 一方是元素   寻找。这降低了平均复杂度O(n log n)(in   快速排序)到O(n)(快速选择)。

     

与quicksort一样,quickselect通常作为就地实现   算法,除了选择第k个元素,它也是部分的   对数据进行排序。有关详细信息,请参阅selection algorithm   与排序的联系。

要将数组的元素分成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,您实际上对kth元素周围的数组进行了部分排序(请注意,这与实际排序整个数组不同)就地和平均情况线性时间。

快速选择的时间复杂性:

  1. 最差情况表现O(n 2
  2. 最佳案例表现O(n)
  3. 平均案例表现O(n)
  4. quickselect的运行时几乎总是线性的而不是二次的,但这取决于这样的事实:对于大多数数组,只需选择一个随机轴点几乎总会产生线性运行时。但是,如果要改善快速选择的最坏情况性能,可以选择在每次调用之前使用median of medians algorithm来近似用于快速选择的最佳枢轴。这样,您可以将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()
    

答案 1 :(得分:1)

我会看看order statistics。统计样本的第k阶统计量等于其第k个最小值。计算列表中第k个最小(或最大)元素的问题称为选择问题,由selection algorithm解决。

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

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

答案 2 :(得分:0)

分配与输入数组大小相同的数组 扫描输入数组一次并跟踪数组的最小值和最大值。 并同时将第二个数组的所有值设置为1。 计算delta = (max - min) / 3。 再次扫描数组,如果数字为> min+delta and < max-delta,则将第二个数组设置为2;否则if >= max-delta,将其设为3; 因此,您将拥有一个数组,告诉该数字是哪个组。 我假设所有数字彼此不同。 复杂性:O(2n)