【问题标题】:Convert a row and column sorted 2-D array to a sorted 1-D array将行列排序的二维数组转换为排序的一维数组
【发布时间】:2012-09-18 19:07:10
【问题描述】:

给定一个包含 n*n 个元素的二维数组:

  • 所有行都已排序
  • 所有列都已排序

例如:

1 5 7
2 6 8
3 9 10

将其转换为一维排序数组。有没有比O(nlog(n))更好的解决方案。

【问题讨论】:

  • 这里不是发布硬件的地方
  • @Noam:这不是作业问题。这是一道面试题。我已经考虑了一段时间,但无法提出解决方案。

标签: algorithm matrix array-algorithms


【解决方案1】:

它的复杂度不能低于n^2,因为您有n^2 元素,每个元素至少要检查一次。

执行此操作的算法是标准的k路合并,其复杂度为N log k,其中N是要合并的项目总数,k是序列数。

在您的 n*n 数组中,您有要合并的 n*n 项目和 n 序列(即行)。所以,代入上式:

N log k
N = (n*n)
k = n
(n*n) log n

将您的二维数组转换为已排序的一维数组是n^2 log n

【讨论】:

    【解决方案2】:

    您可以使用函数 f(x,y) = (f(x),f(y)) 来降低复杂性并展平二维数组。它还对一维数组重新排序。因为它是一个函数,所以它更像是一个散列算法,也许不是你要找的?

    【讨论】:

    • 你能解释一下 f(x,y) = (f(x),f(y)) 是什么意思吗?
    【解决方案3】:

    您应该发布您的O(n log n) 解决方案:事实是,没有这样的解决方案真的是可能的。这就是为什么。你有n*n 元素。所以你能做的最好的就是n^2,因为你必须至少访问每个元素一次。考虑一下。您必须填充长度为n*n 的一维数组(即,二维数组的每个元素都必须存在于新的一维数组中)。因此在O(n log n) 中没有办法解决这个问题。

    【讨论】:

      【解决方案4】:

      我有一个时间:O(n*n log(2n)) 空间 O(2n) 算法。

      基本思路如下

      a[0][0] 肯定是最小的元素。因为元素是按行和按列排序的,所以下一个最小的元素将是 min( a[0][1], a[1][0] )。比如说,a[0][1] 是两者中最小的,下一个候选者将是 min(a[0][2],a[1][1],a[1][0])

      等等,

      算法:

      选择最小的候选元素,打印它。将元素推到最小元素的右侧和底部作为潜在候选者。 (检查出局或绑定。) 这样做直到不再存在候选元素。

      需要 DS:

      可以使用堆来维护候选元素(顶部是最小元素)但是你需要确保你不会两次推送相同的元素。 (y 可能在 x 的右侧和 z 的底部)。 在你推送到堆之前,你需要知道元素是否已经被推送。

      这两个要求都由 set 优雅地处理(这是有序的)(我在 c++ 中编写代码)

      在集合中存储索引可以让我轻松地将下一个候选元素推送到候选 Ds。
      在我的候选 ds 中,我在任何时间点都保持不超过 n+n 个元素 以下是我使用的比较功能。

      bool operator()(const int& a,const int& b) const{
              int ai=a/m,aj=a%m;
              int bi=b/m,bj=b%m;
              if(input[ai][aj]!=input[bi][bj])
              return input[ai][aj]<input[bi][bj];
              else
              return a<b;
          }
      

      这里有一个示例实现:http://ideone.com/w208Af

      【讨论】:

        【解决方案5】:

        你可以在 O(n) 中做到这一点。我不同意这不可能比 n^2 更快,因为在这种情况下 n*n=n 因为当您谈论时间复杂度时,您指的是列表中元素的数量。这是 O(n) 解决方案,其中 n = min-max。为此,您需要知道可能在列表中的最小数字和最大数字。您不需要知道它们是最低的和最高的。

            int[] Sorted1dArray(int[][] arr, int min, int max)//basically counting sort
            {
               int index = 0;
               int rowColSize = arr[0].Length;
               int[] newArr = new int[max-min];
               int[] ret = new int[rowColSize *rowColSize ];
               for(int i=0;i<rowColSize ;i++)//
               {
                  for(int j=0;j<rowColSize ;j++)
                     newArr[arr[i][j]]++;
               }
               for(int i=0;i<newArr.Length;i++)
               {
                  for(int j=0;j<newArr[i];j++)
                  {
                     ret[index++]=newArr[j];
                  }
               }
               return ret;
            }
        

        【讨论】:

        • n*n=n 会混淆这个问题。无论如何,如果您有 k 个包含总共 n 个项目的排序列表,则将它们合并到一个排序列表中需要 O(n log k) 时间。在一般情况下,你不能比这更快。
        • 上面的代码是线性时间,n=min-max。它遍历列表一次,然后在一次迭代中填充 newArr,然后再遍历 newArr 一次以创建 ret。你是说这是假的吗?
        • 您的代码有几个错误。首先,当针对 OP 的数据运行它尝试索引 newArr[10] 时,它会抛出 IndexOutOfRangeException。并且所有第一组循环所做的(如果有问题的项目是 0 到 8,它会起作用)是将newArr 中的每个项目初始化为 1。也就是说,如果存在,您的计数排序想法可以在 O(n) 中起作用是 n 个顺序的数字,使得max-min+1 == n,并且没有重复。但这些条件不适用于 OP 的示例。
        • 你说得对,我犯了一个小错误。但是重复并不重要。这就是为什么每次有重复时都会增加索引的原因。因此,例如,如果所有数字都是 1,您将有一个数组,其中第一个索引的值为 9。要纠正我的错误,您必须将数组设置为具有最大值的大小,在这种情况下为 10 . 感谢您的反馈。
        【解决方案6】:

        我认为这将是我将采取的方法:

        1) 从第一行开始 - “1 5 7”。我们知道,在开始时,行是排序的,所以我们保证最左边的元素是最少的,不需要比较。输出 1,并将第一列向上移动,得到“2 5 7”。

        2) 现在比较前两个元素。只要第一个元素小于第二个元素,就继续输出第一个元素并将其相应的列向上移动。这给了我们输出 2 和 3 的结果数组为“5 7”(因为第一列现在是空的)。

        3) 对剩下的两列继续做同样的事情。在遇到第一种不同的情况之前,这将为我们提供 5 和 6 的输出 - 此时我们的数组将包含“9 7”。

        4) 在这种情况下,我们将输出最后一列的值并将其向上移动,在输出 7 后给我们“9 8”。我们再次遇到第一个元素更大的情况,所以我们'将输出第二个并将该列向上移动,留下“9 10”。

        尚未彻底分析,但我认为这是 O(N),因为您可以对各个列之间的关系做出假设,因此最多可以进行 N 次比较。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-07-26
          • 2021-06-28
          • 1970-01-01
          • 2012-04-03
          • 1970-01-01
          • 1970-01-01
          • 2021-09-25
          • 2021-10-20
          相关资源
          最近更新 更多