我有一个以下的面试问题。
有一系列nxn元素。数组是部分排序的,即行i
中的最大元素小于行i+1
中的最小元素。
如何找到复杂度为O(n)
以下是我对此的看法:
你应该去n / 2行。并开始比较,例如你搜索100,你看到的第一个数字是110,所以你知道它在这一行或上面的行中现在你去n / 4所以上。
来自评论
总共不是O(n * log n)吗?他有 解析他的每一行 因此,达到每个二进制搜索 线性搜索的数量是 乘以他的行数 必须平均扫描。 - 马丁 Matysiak 5分钟前。
我不确定这是一个正确的解决方案。有没有人有更好的东西
答案 0 :(得分:14)
您的解决方案确实需要O(n log n)
,假设您正在搜索您解析的每一行。如果不搜索每一行,则无法准确执行二进制步骤。
O(n)
解决方案:
选择n/2
行,而不是搜索整行,我们只需要获取前一行的第一个元素,以及下一行的第一个元素。 O(1)
。
我们知道n/2
行的所有元素必须在这些选定值之间(这是关键观察)。如果我们的目标值位于区间中,则搜索所有三行(3*O(n) = O(n)
)。
如果我们的值超出此范围,则继续采用二进制搜索方式,如果我们的值小于范围则选择n/4
,如果值更大则选择3n/4
行,并再次比较对着相邻行的一个元素。
找到3行的正确块会花费O(1) * O(log n)
,找到该元素将花费O(n)
。
总计O(log n) + O(n) = O(n)
。
答案 1 :(得分:3)
这是一个简单的实现 - 因为无论如何我们需要 O(n)来查找行中的元素,我省略了bin-search ......
void search(int n[][], int el) {
int minrow = 0, maxrow;
while (minrow < n.length && el >= n[minrow][0])
++minrow;
minrow = Math.max(0, minrow - 1);
maxrow = Math.min(n.length - 1, minrow + 1);
for (int row = minrow; row <= maxrow; ++row) {
for (int col = 0; col < n[row].length; ++col) {
if (n[row][col] == el) {
System.out.printf("found at %d,%d\n", row, col);
}
}
}
}