按行和列排序的矩阵。我们需要从给定矩阵中找到Kth
最小元素。
有一个时间复杂度为O(KlogM)
但需要线性时间算法的解决方案。
示例矩阵:
1 3 5
2 4 6
7 8 9
假设M =没有行,N =没有列,M<N
。
我的解决方案:
- 通过获取所有
matrix[i][0]
元素来创建大小为M的堆。- 找到最小的元素并从相应的行中推送下一个元素。
- 重复第二步,直至找到
醇>Kth
最小。
答案 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)
,以及两个变量lessthanleft
和greaterthanright
,具有以下属性:
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
。请注意,lessthanleft
是lessthanleft
所指示位置左侧位置的矩阵元素数 - 即严格小于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]
所在的位置。i
到N - right[i]
的{{1}}值i
的总和为0
。请注意,M - 1
是greaterthanright
所指示位置右侧位置的矩阵元素数 - 即严格大于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 - 1
,A[M-1, N-1]
,i0=0
相对应。
现在,在每次迭代中,我们选择一个带有j0=0
的枢轴矩阵元素i1=M-1
。 (有几种策略;我将在下面讨论这个。)我们使用数组j1=N-1
和A[i2, j2]
,我们将填充以下内部循环,并设置变量left[i2] <= j2 < right[i2]
和less[0..M)
和lessequal[0..M)
到nless
。在内部循环中,我们遍历所有行nequal
,并设置ngreater
和0
,以便i
和less[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
。或者,您可以对每行中其余条目的中位数进行中位数样式计算。
通常,每行上0
和i
之间的矩阵元素集合会被分割,希望在每次迭代时大致为一半。复杂性分析类似于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
。继续我们得到17
和less[0]
。因此less[1] = 2
,less = [2,2,1,0]
和lessequal = [4,3,2,0]
。我们有nless = 5
,因此我们会继续使用大于nequal = 4
的条目并设置ngreater = 11
和lessthanleft + nless + nequal = 9 < 11
。
对于下一次迭代,我们需要在17
和left = lessequal = [4,3,2,0]
之间的中心的某行[{1}}上选择一个数据透视表。也许我们选择行lessthanleft = 9
,这意味着我们有i
。在内循环期间,我们现在left[i]
和right[i]
获得2
和A[2,3] = 29
以及less = [5,4,3,1]
。现在lessequal = [5,4,4,2]
,我们继续使用小于nless = 4
的条目并设置nequal = 2
和ngreater = 5
。
对于第三次迭代,现在每行只剩下一个条目。我们随机选择一行 - 可能是行lessthanleft + nless = 13 > 11
。枢轴是29
。在内循环期间,我们得到right = less = [5,4,3,1]
和greaterthanright = 7
。因此3
,A[3,0] = 23
和less = [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
值。