具有排序行和列的矩阵中的第K个最小元素

时间:2013-09-26 12:42:10

标签: java algorithm data-structures matrix

按行和列排序的矩阵。我们需要从给定矩阵中找到Kth最小元素。

有一个时间复杂度为O(KlogM)需要线性时间算法的解决方案。

示例矩阵:

1   3    5
2   4    6
7   8    9

假设M =没有行,N =没有列,M<N

我的解决方案:

  
      
  1. 通过获取所有matrix[i][0]元素来创建大小为M的堆。
  2.   
  3. 找到最小的元素并从相应的行中推送下一个元素。
  4.   
  5. 重复第二步,直至找到Kth最小。
  6.   

2 个答案:

答案 0 :(得分:3)

Frederickson和Johnson算法的工作原理大致如下。 (它总是让我想起Quickselect。)这是来自一个使用不同实现作为模板的实现,所以细节可能与他们的论文有点不同,但它是同一时间的复杂性和相同的理念。我假设k的合法值从0运行到M * N - 1

我们将i行和j列中的元素作为A[i, j]访问(从0开始计算)。此外,我们将假装我们可以访问负无穷的A[i, -1]和正无穷的A[i, N]。我们维护两个索引数组left[0 .. M)right[0..M),以及两个变量lessthanleftgreaterthanright,具有以下属性:

  • 存在矩阵元素A[i0, j0],因此对于所有行i,我们都有A[i, left[i] - 1] < A[i0, j0] <= A[i, left[i]]。也就是说,left[i]大致是行A[i0, j0]i所在的位置。
  • left[i]i的{​​{1}}值0的总和为M - 1。请注意,lessthanleftlessthanleft所指示位置左侧位置的矩阵元素数 - 即严格小于left的矩阵元素。
  • A[i0, j0]。所以这表示我们正在寻找的元素位于lessthanleft <= k右侧的某一行i
  • 存在矩阵元素left[i],因此对于所有行A[i1, j1],我们都有i。也就是说,A[i, right[i] - 1] < A[i1, j1] <= A[i, right[i]]大致是行right[i]A[i1, j1]所在的位置。
  • iN - right[i]的{​​{1}}值i的总和为0。请注意,M - 1greaterthanright所指示位置右侧位置的矩阵元素数 - 即严格大于greaterthanright的矩阵元素。
  • right。所以这表示我们正在寻找的元素位于A[i1, j1]左侧的某一行greaterthanright <= M * N - k

可以通过将i的每个元素设置为right[i],将left的每个元素设置为0,以及right和{{}来初始化这些属性1}}到N - 除非lessthanleft(在这种情况下我们返回greaterthanright)或0(在这种情况下我们返回k = 0)。顺便说一句,这与A[0, 0]k = M*N - 1A[M-1, N-1]i0=0相对应。

现在,在每次迭代中,我们选择一个带有j0=0的枢轴矩阵元素i1=M-1。 (有几种策略;我将在下面讨论这个。)我们使用数组j1=N-1A[i2, j2],我们将填充以下内部循环,并设置变量left[i2] <= j2 < right[i2]less[0..M)lessequal[0..M)nless。在内部循环中,我们遍历所有行nequal,并设置ngreater0,以便iless[i]。 (自lessequal[i]起,您可以使用A[i, less[i] - 1] < A[i2, j2] <= A[i, less[i]]A[i, lessequal[i] - 1] <= A[i2, j2] < A[i, lessequal[i]]的值,并从那里开始线性搜索,以便整体花费less[i-1] >= less[i]时间。)我们将less[i-1]添加到lessequal[i-1],我们将O(N)添加到less[i] - left[i],然后我们将nless添加到lessequal[i] - less[i]

在这个内循环之后,我们检查是否nequal。如果是这种情况,我们会通过将right[i] - lessequal[i]设置为ngreater来继续使用小于lessthanleft + nless >= k的条目(如果要阻止在数组的循环中分配,则通过指针翻转,或者将值从A[i2, j2]复制到right)和less复制为less,然后继续下一次迭代。如果right,则我们会将greaterthanright设为greaterthanright + ngreater + nequal,将lessthanleft + nless + nequal < k设为A[i2, j2],然后继续输入大于left的条目下一次迭代。否则,我们要查找的条目属于等于lessequal的条目,因此我们返回lessthanleft

现在关于选择枢轴。一种方法是在lessthanleft + nless + nequal区间内选择一个随机数A[i2, j2];然后,我们在A[i2, j2]c之间找到包含[0 .. N * M - lessthanleft - greaterthanright)矩阵元素的行,从c开始,从每个left减去right left[i] - right[i]直到变为负面。现在选择它变为负数的c并让i。或者,您可以对每行中其余条目的中位数进行中位数样式计算。

通常,每行上0i之间的矩阵元素集合会被分割,希望在每次迭代时大致为一半。复杂性分析类似于Quickselect,其中预期的迭代次数是“几乎可以肯定的”#34; (在数学意义上)对数初始池中的值的数量。通过使用中位数样式的枢轴选择,我相信你可以在实际运行时支付它时确定地实现这一目标,但我现在假设&#34;几乎可以肯定&#34;够好了。 (这与Quicksort为O(N lg(N))的条件相同。)任何单独的迭代都需要j = round((left[i] + right[i])/2)时间来选择一个轴,然后在内部循环中left[i]时间。初始候选人群有right[i]个成员,因此迭代次数几乎肯定是O(M),总复杂度为O(N)。使用带有每个行的条目的堆的算法需要M*N,这要差得多,因为O(lg(M * N)) = O(lg(M) + lg(M))仅受O(N * (lg(M) + lg(N)))限制。


以下是一个简单示例:考虑O(k * lg(M))k,我们需要从矩阵N*M中选择M=4元素,如下所示:

N=5

我们将k=11初始化为A,将 6 12 17 17 25 9 15 17 19 30 16 17 23 29 32 23 29 35 35 39 初始化为left,将[0,0,0,0]right初始化为[5,5,5,5]

假设在第一次迭代中我们选择lessthanleft作为枢轴。在内部循环期间,我们开始检查从条目greaterthanright到左边的第一行,找到最多出现一个最多0的数字。这是列A[1, 2]=17,因此我们设置了right[0]-1=4。我们现在继续搜索第一次出现的严格小于17的数字;这发生在3列中,因此我们设置了lessequal[0]=3+1=4。对于下一行,我们可以开始在列17处查找最多1的值,我们会在列less[0]=1+1=2中找到它,因此请设置17。从lessequal[0]开始寻找严格小于2的值,我们设置lessequal[1]=2+1=3。继续我们得到17less[0]。因此less[1] = 2less = [2,2,1,0]lessequal = [4,3,2,0]。我们有nless = 5,因此我们会继续使用大于nequal = 4的条目并设置ngreater = 11lessthanleft + nless + nequal = 9 < 11

对于下一次迭代,我们需要在17left = lessequal = [4,3,2,0]之间的中心的某行[{1}}上选择一个数据透视表。也许我们选择行lessthanleft = 9,这意味着我们有i。在内循环期间,我们现在left[i]right[i]获得2A[2,3] = 29以及less = [5,4,3,1]。现在lessequal = [5,4,4,2],我们继续使用小于nless = 4的条目并设置nequal = 2ngreater = 5

对于第三次迭代,现在每行只剩下一个条目。我们随机选择一行 - 可能是行lessthanleft + nless = 13 > 11。枢轴是29。在内循环期间,我们得到right = less = [5,4,3,1]greaterthanright = 7。因此3A[3,0] = 23less = [4,4,2,0]。我们现在有lessequal = [4,4,3,1],因此我们返回nless = 1。实际上,有10个矩阵条目严格小于nequal = 2(最后一次迭代中ngreater = 1左侧的那些)和12个最多lessthanleft + nless = 10 < k = 11 <= lessthanleft + nless + nequal = 12的矩阵条目(左边的那些)在最后一次迭代中23。)

答案 1 :(得分:-1)

我认为您可以编写一个O(k)算法,从当前最小值“跳转”到下一个算法,从而最多进行k次操作。您可以使用2个引用,一个指示所有列的当前最小值,另一个指示所有行的当前最小值。然后,您可以按行或按列前进每次迭代。在k次迭代之后,您将获得所需的Kth值。