【问题标题】:Memory and time-efficient sorting of all rotations of a long array长数组所有旋转的内存和高效排序
【发布时间】:2020-03-04 16:58:06
【问题描述】:

我正在寻找以下问题的内存和省时解决方案。很容易想到一个使用 O(n2) 内存和 O(n2 log n) 时间(?)的基本算法,我想知道如果有一种算法显着提高内存和时间效率(对于某些输入结构)。

输入:长度为 n 的数组 a 填充有非负整数。数组中的每个整数的大小都小于或等于 b。我对 n 较大(数千万)且 b 与 n(数百)相比相对较小的设置感兴趣。我们可以假设 a 中任何特定整数的出现次数都以 m 为界,其中 m 的数量级为 n/b。

考虑数组 a 的所有旋转的集合,即如果 a = (a1, a2, ..., an), 那么它的旋转是 (a1, a2, ..., an), (a 2, a3, ..., an, a1), (a3, a4, ..., an, a1, a2), ... , (an, a1, a2, ..., an-1)。令 r1, r2, ..., rn 表示数组 a 的旋转按字典顺序(递增)排序(使用0

输出:两个数组firstlast,分别存储r1、r的第一个和最后一个元素2, ..., rn 按排序顺序。

基本算法(不是内存或时间效率):构造 a 的所有旋转并将它们存储在矩阵 A 中(使用 O(n2) 内存) .定义一个排序例程(使用标准排序技术)对 A 进行排序。提取排序矩阵的第一列和最后一列。推测排序步骤的最佳时间复杂度为 O(n2 log n),即排序算法的 O(n log n) 乘以每个成对比较的 O(n)。

第二种算法:我们可以遍历数组并在其中存储以小于或等于 b 的任何整数 i 开头的位置(这是 O(n) 时间和 O(nm ) 记忆操作)。然后,我们可以从一个特定的整数 i 开始生成所有旋转,并按照基本算法对这个旋转矩阵进行排序(存储这个矩阵使用 O(nm) 内存,排序大概需要 O(mn log m) 时间)。通过对从 0 到 b 的每个整数 i 重复该过程,很容易构造输出。

是否有一种使用定制排序策略的内存效率更高的方法?我很有希望,因为存储输出只需要 O(n) 内存。

【问题讨论】:

  • 我认为你的第一个算法很好,但你不必创建整个矩阵。通过偏移量来识别每个旋转并调整排序中使用的比较应该就足够了。或者,将数组加倍 (1, 2, 3, ..., n, 1, 2, 3, ...) 并使用标准排序算法将切片/视图排序到该数组中。
  • @MOehm 感谢您的建议。那么 O(n^2 log n) 在时间复杂度上我能做到的最好吗?
  • 为什么是 O(n² log n)?排序应该是 O(n log n)。创建要排序和输出的向量是 O(n)。额外的内存是 O(n)。
  • @MOehm 排序步骤的数量可以是 O(n log n) 并且每个成对比较是 O(n),不是吗?也许成对比较实际上更快
  • 啊,我明白了。但 O(n) 是比较的上限。

标签: arrays algorithm sorting rotation memory-efficient


【解决方案1】:

在 O(nlogn) 中为给定数组计算 suffix array。请注意,链接实现对循环移位进行排序(特殊字符用于后缀 - 你不需要它) - 正是你需要的(但“排序”没有物理排序)

然后从后缀数组中提取由索引寻址的初始数组元素 - 这是需要的起始元素列表(结束元素位于先前的索引处)。

P[0] 包含最小循环移位的位置,因此A[P[0]]A[P[0]-1] 是第一对,P[1] - 下一个最小移位的位置,依此类推

【讨论】:

    【解决方案2】:

    让我们构建一个更好的算法来找到第一个旋转。按字典顺序构造最后一个是同样的问题,但排序相反。我们需要跟踪的是最小旋转是多少,以及我们当前可能处于什么位置。这是实现这个想法的 Python。

    def min_rotation (elements):
        rotation = []
        positions = [i for i in range(len(elements))]
        while len(rotation) < len(elements):
            min_element = elements[positions[0]]
            new_positions = []
            for i in range(len(positions)):
                # Get the position and start rotating.
                position = positions[i]
                if elements[position] < min_element:
                    min_element = elements[position]
                elif min_element <elements[position]:
                    continue
                new_position = (position + 1) % len(elements)
                new_positions.append(new_position)
            positions = new_positions
            rotation.append(min_element)
        return rotation
    

    最坏的情况是O(n) 内存和O(n*n) 运行时间。你会在一个常量数组上找到它。

    预期的情况将是 O(n) 运行时间,因为 positions 的数量迅速向 1 移动。

    【讨论】:

      猜你喜欢
      • 2017-01-26
      • 2015-11-10
      • 2017-12-28
      • 1970-01-01
      • 2011-06-14
      • 1970-01-01
      • 2018-09-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多