Algorithm 高效地查找数组中元素的秩?

Algorithm 高效地查找数组中元素的秩?,algorithm,sorting,statistics,performance,space-efficiency,Algorithm,Sorting,Statistics,Performance,Space Efficiency,如何有效地求出数组中每个元素的秩(在ties情况下求平均值)?例如: float[] rank(T)(T[] input) { // Implementation } auto foo = rank([3,6,4,2,2]); // foo == [3, 5, 4, 1.5, 1.5] 我能想到的唯一方法是分配3个阵列: 输入数组的副本,因为它必须排序,而我们不拥有它 用于跟踪输入数组排序顺序的数组 要返回的列组数组 有人知道如何在O(N logn)时间和O(1)辅助空间(意味着我

如何有效地求出数组中每个元素的秩(在ties情况下求平均值)?例如:

float[] rank(T)(T[] input) {
    // Implementation
}

auto foo = rank([3,6,4,2,2]);  // foo == [3, 5, 4, 1.5, 1.5]
我能想到的唯一方法是分配3个阵列:

  • 输入数组的副本,因为它必须排序,而我们不拥有它
  • 用于跟踪输入数组排序顺序的数组
  • 要返回的列组数组

  • 有人知道如何在O(N logn)时间和O(1)辅助空间(意味着我们必须分配的唯一数组是我们要返回的数组)中执行此操作吗?或者至少要去掉上面三个数组中的一个?

    如果您不拥有该数组,我认为不可能在O(N logn)和空间O(1)中执行此操作

    如果元素范围(元素可以有多大)很小,请使用计数。计算每个元素的数量,然后使用计数数组根据输入数组计算结果数组

    c - is counting result,
    C - is cumulative counting
    C[i] = c[i] + c[i-1] + c[i-2] + ... + c[0]
    result[i] = 1 / c[in[i]] + C[in[i]-1]
    

    为什么不直接复制和排序数组,然后从那里开始呢?有很多就地排序算法可用,比如heapsort。

    好的,所以您可以将输入数组复制到
    foo
    中。排序
    foo
    在O(n log n)时间内使用。现在,使用输入数组的第一个元素,在O(logn)时间内查找其在
    foo
    中的秩,并将秩插入
    ranks
    数组并返回它


    现在,您使用2个数组而不是3个。

    您可以分配要返回的数组(我们称之为R),将其初始化为0..n-1,然后对传入数组(称为I)进行“排序”,但使用比较I[R[k]]与I[R[j]]而不是普通的R[k]与R[j],然后根据需要交换R数组中的值(而不是像往常一样使用I数组中的值)

    您可以使用quicksort或heapsort(或bubblesort)实现这种间接排序,但这会搞乱您的复杂性


    您只需要为索引分配一个数组和一些堆栈空间。

    也许用一些简单的代码总结一下(以及相关的注释)会很有用

    以下是如何在Ruby中实现这一点:

    arr = [5,1,0,3,2,4]
    ranks = (0..arr.length-1).to_a.sort_by{ |x| arr[x] }
    # ranks => [2, 1, 4, 3, 5, 0]
    
    在Python中:

    arr = [5,1,0,3,2,4]
    ranks = range(len(arr))
    ranks.sort(key=lambda x:arr[x])
    # ranks => [2, 1, 4, 3, 5, 0]
    

    秩数组告诉您0有秩2,1有秩1,2有秩4,等等(当然,这些秩从零开始,而不是从一开始)

    使用二元搜索树并将元素逐个插入到BST中如何。然后,在元素节点左侧的所有元素上保留一个计数器,即可确定秩,我们希望找到秩,以便遍历BST。

    在python中,我使用此方法快速且肮脏:

    def rank(X):
        B = X[:]
        B.sort()
        return [ float(B.index(x)+1) for x in X]
    
    def rank(X):
        B = X[:]
        B = list(set(B))
        B.sort()
        return [ float(B.index(x)+1) for x in X]
    

    第一个示例适用于原始列表中没有重复项的情况。它可以做得更好,但我尝试了一些技巧并得出了这个结论。第二个示例适用于有重复项的情况。

    你说的“我们不拥有它”是什么意思?实际上,你可能不需要第二个数组,因为排序数组中的查找是O(logn)并且您需要N次查找,这在O(N logn)要求范围内工作。“我们不拥有它”=这是一个库函数,它必须假设rank()的调用方不希望其输入数组被无故重新排序,因此根据最小惊奇原则,我们必须复制它并在副本上排序。您不能有O(1)辅助空间!必须有O(n)因为数组的大小是可变的。事后看来很明显。为什么我没有想到这一点?事实上,经过再三考虑,这不起作用。我最初误解了它。是的,它起作用了-你需要间接比较,然后更新间接数组。我在我的帖子中添加了一个说明。除了“平均值”之外,它工作得很好要求。你可能需要第二次通过。Matthieu:你能解释一下吗?这个方法对数组中的负元素不起作用:试试arr=[5,1,0,3,-2,4]--你得到[4,2,1,3,5,0]