Algorithm 排序矩阵的选择算法
这是一个谷歌面试问题: 给定一个N*N矩阵。 所有行都已排序,所有列都已排序。 求矩阵的第k个最大元素 在n^2中进行排序很简单,我们可以使用堆或合并排序(nlgn)对其进行排序,然后得到它,但是有比(nlgn)更好的方法吗 数组的示例::Algorithm 排序矩阵的选择算法,algorithm,Algorithm,这是一个谷歌面试问题: 给定一个N*N矩阵。 所有行都已排序,所有列都已排序。 求矩阵的第k个最大元素 在n^2中进行排序很简单,我们可以使用堆或合并排序(nlgn)对其进行排序,然后得到它,但是有比(nlgn)更好的方法吗 数组的示例:: 1 5 7 12 3 6 8 14 4 9 10 15 11 17 19 20 1将矩阵顺时针旋转45度。您将得到一个菱形的数据集。高度为2N-1,从顶部开始的每一行中的元素数量为:1,2,3,4,5,4,3,2,1
1 5 7 12
3 6 8 14
4 9 10 15
11 17 19 20
1将矩阵顺时针旋转45度。您将得到一个菱形的数据集。高度为2N-1,从顶部开始的每一行中的元素数量为:1,2,3,4,5,4,3,2,1,表示N=5 您将发现,一行中的每个数字总是大于上面的任何数字 对于第k行(从1开始计算),k
由于N=sqrt(N),该算法的总成本为O(sqrt(N)ln(sqrt(N)),示例中给出了矩阵: 如果要搜索第7个元素,您知道第7个元素位于元素M[4][1..4],M[1..4][4]中。您将获得两个已排序的数组,即12,14,15,20和11,17,19,它们可以合并。然后应用二进制搜索,即O(log N) 概括:对于该矩阵中的第k个最大元素,您必须选择适当的层:[2N-1]+[2(N-1)-1]+…>=k,因此选择适当层的算法是Sum[2(N-i)-1]>=k,对于i=0,N-1,其中i是层的编号。找到i,层编号后,您将有2(N-i)-1该数组中必须合并然后搜索的元素。搜索该层的复杂性为O(log[2(N-i)-1]=O(log(N-i)) 算术级数导致 0>=i^2-2*N*i+k i1,2=N+-sqrt(N^2-k),其中k是我们搜索的元素…是的,由于Frederickson和Johnson,有一个O(k)算法
Greg N.Frederickson和Donald B.Johnson.《广义选择和排序:排序矩阵》《暹罗计算机杂志》13,第14-30页。基于N,可以找到元素所在的对角线。例如,在矩阵中
1 5 7 12
3 6 8 14
4 9 10 15
11 17 19 20
您可以通过确定前面对角线中元素的总数来推断对角线
/diagonal#/elements/# of elements/cumulative # of elements/
/d1/ 1 / 1 / 1 /
/d2/ 3 5 / 2 / 1+2 = 3 /
/d3/ 4 6 7 / 3 / 1+2+3 = 6 /
/d4/ 11 9 8 12 / 4 / 1+2+3+4 = 10 /
/d5/ 17 10 14 / 3 /
/d6/ 19 15 / 2 /
/d7/ 20 / 1 /
我们需要找到对角线的原因是,上面的对角线总是有小于当前对角线元素的元素,下面的对角线总是有大于当前对角线元素的元素
因此,您可以确保对角线
d4
具有所需的元素(因为它包含第7大到第10大的元素)。因为直到上一条对角线有6个元素,您只需在对角线d4
中找到第4大元素即可从(0,0)开始进行呼吸优先搜索。(0,0)的2个子元素(0,1)和(1,0)添加到第二个元素的潜在候选元素列表中。循环选择潜在候选元素列表中最小的元素作为下一个元素,将其子元素添加到潜在候选元素列表中。找到第k个元素时停止
使潜在候选列表成为最小堆。堆永远不会大于n+m
如果k大于n*m/2,您也可以从最后一个元素(n,m)开始反向操作
最坏情况:这将是n*m/2 lg(n+m),而不是n*m lg(n*m)的排序。如果您注意到:
enum Direction {
X,
Y
};
struct Index
{
Index(int unsigned x, int unsigned y)
: x(x),
y(y)
{}
void operator = (Index const & rhs)
{
x = rhs.x;
y = rhs.y;
}
int unsigned x;
int unsigned y;
};
int unsigned solve(int unsigned i_k, int unsigned ** i_data, int unsigned i_n)
{
if (1 == i_k) {
return i_data[0][0];
}
Direction dir = X;
Index smaller(0,0);
Index larger(0,0);
if (i_data[1][0] < i_data[0][1]) {
dir = X;
smaller = Index(1,0);
larger = Index(0,1); }
else {
dir = Y;
smaller = Index(0,1);
larger = Index(1,0);
}
for (int unsigned i = 0; i < (i_k - 2); ++i) {
int unsigned const x = smaller.x;
int unsigned const y = smaller.y;
if (X == dir) {
if ((x + 1) == i_n) {
// End of row
smaller = larger;
larger.x += 1;
dir = Y; }
else if (i_data[x + 1][y] < i_data[larger.x][larger.y]) {
smaller.x += 1; }
else {
smaller = larger;
larger = Index(x + 1, y);
dir = Y;
} }
else {
if ((y + 1) == i_n) {
// End of col
smaller = larger;
larger.y += 1;
dir = X; }
else if (i_data[x][y + 1] < i_data[larger.x][larger.y]) {
smaller.y += 1; }
else {
smaller = larger;
larger = Index(x, y + 1);
dir = X;
}
}
}
return i_data[smaller.x][smaller.y];
}
下面是我的C++解决方案,它是基于一个最小堆的。当一个矩阵中的一个单元在最小堆的顶部时,右边和/或下边的数字将被插入堆中。
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
struct Entry {
int value;
int x;
int y;
bool operator < (const Entry& other) {
return this->value > other.value;
}
};
bool getKthNumber(int* matrix, int row, int col, int k, int* result){
if(matrix == NULL || row <= 0 || col <= 0 || result == NULL)
return false;
if(k <= 0 || k > row * col)
return false;
vector<Entry> minHeap;
Entry first = {matrix[0], 0, 0};
minHeap.push_back(first);
make_heap(minHeap.begin(), minHeap.end());
for(int i = 0; i < k; ++i){
first = minHeap[0];
int x = first.x;
int y = first.y;
if(first.y == 0 && first.x < row - 1){
Entry next = {matrix[(x + 1) * col], x + 1, y};
minHeap.push_back(next);
push_heap(minHeap.begin(), minHeap.end());
}
if(first.y < col - 1){
Entry next = {matrix[x * col + y + 1], x, y + 1};
minHeap.push_back(next);
push_heap(minHeap.begin(), minHeap.end());
}
pop_heap(minHeap.begin(), minHeap.end());
minHeap.pop_back();
}
*result = first.value;
return true;
}
#包括
#包括
#包括
使用名称空间std;
结构条目{
int值;
int x;
int-y;
布尔运算符<(常数输入和其他){
返回此->值>其他.value;
}
};
bool getKthNumber(int*矩阵、int行、int列、int k、int*结果){
如果(矩阵==NULL | |行可能重复,请访问O(n lg n)?这是什么意思?元素的数量为n^2,您希望如何在n log n中对它们进行排序?您可以使用经典的选择算法,在线性时间内找到第k个最小值,而不考虑矩阵的顺序。@Nohsib您的意思是说O(n^2 log n)使用排序?在本例中,菱形的第三行的数字(4)比上面的数字(5)小。这是一个正确且最优的解决方案。我想将其扩展为一个单独的答案,以帮助陷入付费墙的人,但……算法相当混乱。它是渐进最优的,但我认为不是特别实用。它肯定够混乱的,不适合面试的限制。@missingno:不是那么最优。我有一个更有效的方法这个问题的NT平均运行时间解决方案在,它不是一个凌乱的算法。无法访问pdf???-1这个答案是完全无用的,这篇文章不再可用。答案er自从回答这个问题以来一直没有在线。不起作用。看看第三个di中的4
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
struct Entry {
int value;
int x;
int y;
bool operator < (const Entry& other) {
return this->value > other.value;
}
};
bool getKthNumber(int* matrix, int row, int col, int k, int* result){
if(matrix == NULL || row <= 0 || col <= 0 || result == NULL)
return false;
if(k <= 0 || k > row * col)
return false;
vector<Entry> minHeap;
Entry first = {matrix[0], 0, 0};
minHeap.push_back(first);
make_heap(minHeap.begin(), minHeap.end());
for(int i = 0; i < k; ++i){
first = minHeap[0];
int x = first.x;
int y = first.y;
if(first.y == 0 && first.x < row - 1){
Entry next = {matrix[(x + 1) * col], x + 1, y};
minHeap.push_back(next);
push_heap(minHeap.begin(), minHeap.end());
}
if(first.y < col - 1){
Entry next = {matrix[x * col + y + 1], x, y + 1};
minHeap.push_back(next);
push_heap(minHeap.begin(), minHeap.end());
}
pop_heap(minHeap.begin(), minHeap.end());
minHeap.pop_back();
}
*result = first.value;
return true;
}