【问题标题】:Is there an O(n) algorithm to generate a prefix-less array for an positive integer array?是否有 O(n) 算法可以为正整数数组生成无前缀数组?
【发布时间】:2012-04-11 17:58:22
【问题描述】:

对于数组 [4,3,5,1,2], 我们称前缀为 4 的为 NULL,无前缀的 4 为 0; 3 的前缀是 [4],3 的无前缀是 0,因为前缀中没有一个小于 3; 5 的前缀是 [4,3],5 的无前缀是 2,因为 4 和 3 都小于 5; 1 的前缀是 [4,3,5],1 的无前缀是 0,因为前缀中没有一个小于 1; 2 的前缀是 [4,3,5,1],无前缀的 2 是 1,因为只有 1 小于 2

所以对于数组 [4, 3, 5, 1, 2],我们得到 [0,0, 2,0,1] 的无前缀数组, 我们可以通过 O(n) 算法来获得无前缀数组吗?

【问题讨论】:

  • 我问这个是因为我想获得一个 O(n) 来计算给定数组的反转,即使这个数组是在流中出现的。
  • 可能不会。看起来拥有一个无前缀数组使我们能够在 O(n) 时间内对原始数组进行排序。
  • @n.m.:我们如何使用无前缀数组在线性时间内对原始文件进行排序?我看到使用无前缀数组进行排序的方法是将每个元素插入位置 p[i],但不清楚如何在线性时间内完成。
  • @Nabb:我不确定它是否可以在 O(n) 中完成,但它肯定可以在不比较元素的情况下完成。排序是 O(nlog(n)) *comparisons.
  • @n.m. - 这个论点很有希望但很棘手,因为它假设 1) 元素来自无限域,以及 2) 我们拥有的唯一原始操作是常量分支(例如一个元素与另一个元素进行比较,如果我们不允许重复,如果我们这样做的话可能是 3 分支),这一次只给你 1 位信息)。让我们排除 bin 排序,然后理想情况下你应该把它写下来作为答案。

标签: arrays algorithm integer prefix


【解决方案1】:

它不能在 O(n) 中完成,原因与 comparison sort 需要 O(n log n) 比较的原因相同。可能的无前缀数组的数量为n!,因此您至少需要log2(n!) 位信息来识别正确的无前缀数组。 log2(n!)O(n log n),作者是 Stirling's approximation

【讨论】:

  • 不完全正确。比较排序需要 O(n log n),但没有理由需要进行比较排序。例如,您可以使用基数排序对整数数组进行排序。这是 O(k*n) 复杂度。
  • @ShawnH 我并不是说你必须使用比较排序(或任何排序)——只是同样的信息论逻辑适用。在基数排序中,k通常不是位数,大约是log10(n),使其成为O(n log n)?
  • @xan:只有在添加元素必须是不同的约束时才能做出这种暗示。例如,我可以有一个包含十亿个 1 位元素的输入数组。
  • 真@AndrewTomazos-Fathomling。谢谢。少于n个!在这种情况下可能的输出,在您的示例中要少得多。
【解决方案2】:

假设输入元素总是固定宽度的整数,您可以使用基于基数排序的技术来实现线性时间:

  • L 是输入数组
  • X 是当前通道的焦点 L 的索引列表
  • n 是我们目前正在研究的位
  • Count 是当前位置左侧第 n 位的 0 位数
  • Y 是 L 的递归子序列的索引列表
  • P 是一个零初始化数组,它是输出(无前缀数组)

在伪代码中...

Def PrefixLess(L, X, n)
    if (n == 0)
       return;

    // setup prefix less for bit n
    Count = 0

    For I in 1 to |X|
        P(I) += Count
        If (L(X(I))[n] == 0)
            Count++;

    // go through subsequence with bit n-1 with bit(n) = 1
    Y = []
    For I in 1 to |X|
        If (L(X(I))[n] == 1)
            Y.append(X(I))

    PrefixLess(L, Y, n-1)

    // go through subsequence on bit n-1 where bit(n) = 0
    Y = []
    For I in 1 to |X|
        If (L(X(I))[n] == 0)
            Y.append(X(I))

    PrefixLess(L, Y, n-1)

    return P

然后执行:

PrefixLess(L, 1..|L|, 32)

【讨论】:

    【解决方案3】:

    我认为这应该可行,但请仔细检查详细信息。让我们将原始数组 a[i] 中的一个元素和前缀数组中的一个元素称为 p[i],其中 i 是各个数组的第 i 个元素。

    所以,假设我们在 a[i] 并且我们已经计算了 p[i] 的值。有三种可能的情况。如果 a[i] == a[i+1],则 p[i] == p[i+1]。如果 a[i] = p[i] + 1。这给我们留下了 a[i] > a[i+1] 的情况。在这种情况下,我们知道 p[i+1] >= p[i]。

    在幼稚的情况下,我们返回前缀并开始计算小于 a[i] 的项目。但是,我们可以做得更好。首先,认识到 p[i] 的最小值为 0,最大值为 i。接下来看看索引 j 的情况,其中 i > j。如果 a[i] >= a[j],则 p[i] >= p[j]。如果 a[i]

    对该算法进行包络分析,它具有 O(n) 最佳情况下的性能。这是列表已经排序的情况。最坏的情况是它被反向排序。那么性能是O(n^2)。平均性能将是 O(k*n),其中 k 是一个需要回溯的量。我的猜测是对于随机分布的整数,k 会很小。

    我也很确定有办法针对部分排序数据的情况优化此算法。我会查看Timsort 以获取有关如何执行此操作的一些灵感。它使用运行检测来检测部分排序的数据。因此,该算法的基本思想是遍历列表一次并查找数据运行。对于数据的升序运行,您将遇到 p[i+1] = p[i]+1 的情况。对于降序运行,p[i] = p_run[0] 其中 p_run 是运行中的第一个元素。

    【讨论】:

    • 在示例 a[1] 中看到“如果 a[i]
    猜你喜欢
    • 1970-01-01
    • 2018-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多