我昨天的采访中被问到以下问题:
考虑一个Java或C ++数组,说X
,它被排序,并且其中没有两个元素是相同的。如何最好地找到索引i
,使得该索引处的元素也是i
。那是X[i] = i
。
作为澄清,她还给了我一个例子:
Array X : -3 -1 0 3 5 7
index : 0 1 2 3 4 5
Answer is 3 as X[3] = 3.
我能想到的最好的是线性搜索。在采访之后我虽然在这个问题上做了很多但却找不到更好的解决方案。我的论点是:具有required属性的元素可以在数组中的任何位置。所以它也可能在数组的最后,所以我们需要检查每个元素。
我只想在这里向社区确认我是对的。请告诉我我是对的:))
由于
答案 0 :(得分:102)
这可以在O(logN)
时间和O(1)
空间内使用稍加修改的binary search来完成。
考虑一个新的数组Y
,使Y[i] = X[i] - i
Array X : -3 -1 0 3 5 7
index : 0 1 2 3 4 5
Array Y : -3 -2 -2 0 1 2
由于X
中的元素按增加顺序,因此中的元素
新数组Y
将处于非递减顺序。所以一个二进制文件
<{1}}中的0
的搜索将给出答案。
但创建Y
将占用Y
空格和O(N)
时间。而不是
创建新数组只需修改二进制搜索即可
对O(N)
的引用被Y[i]
替换。
算法:
X[i] - i
答案 1 :(得分:9)
有一些更快的解决方案,平均O(log n)或在某些情况下O(log log n)而不是O(n)。有一个谷歌的“二分搜索”和“插值搜索”,你可能会找到很好的解释。
如果数组未排序,那么是,该元素在任何地方,你不能得到O(n),但不是排序数组的情况。
-
根据要求对插值搜索的一些解释:
虽然二分搜索仅涉及以“更大/更大”的方式比较两个元素,但插值搜索也尝试使用数值。要点是:你有一个从0到20000的值的排序范围。你寻找300 - 二进制搜索将从范围的一半开始,为10000.插值搜索猜测300可能会接近0超过20000,所以它会首先检查元素6000而不是10000.然后再次 - 如果它太高,则递归到较低的子范围,并且它太低 - 递归到较高的子范围。
对于具有+ - 均匀分布值的大数组,插值搜索应该比二进制搜索快得多 - 编码并自己查看。 此外,如果首先使用一个插值搜索步骤,然后使用一个二进制搜索步骤,则效果最佳。
请注意,在字典中查找某些内容时,这是人类直观的事情。
答案 2 :(得分:7)
我认为这会更快。
从列表中间开始
如果X [i]>然后我走到剩下的左侧中间
如果X [i]&lt;然后我走到剩下的正中间
继续这样做,它会将每个循环中可能元素的数量减少一半
答案 3 :(得分:7)
不需要像@codaddict在answer中所建议的那样考虑任何数组Y
。
使用二进制搜索并检查给定数组的中间元素,如果它低于其索引,那么我们不需要检查任何较低的索引,因为数组是
排序等等,如果我们向左移动,减去m个索引和(至少)m值,所有后续元素也将太小。例如。如果arr[5] = 4
则arr[4] <= (4 - 1)
和arr[3] <= (4 - 2)
等等。如果中间元素大于其索引,则可以应用类似的逻辑。
这是简单的Java
实施:
int function(int[] arr) {
int low = 0;
int high = arr.length - 1;
while(low <= high) {
int mid = high - (high - low) / 2;
if(arr[mid] == mid) {
return mid;
} else if(arr[mid] < mid) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1; // There is no such index
}
请注意,只有当所有元素都不同时,上述解决方案才有效。
答案 4 :(得分:3)
您可以执行二分查找: 搜索中间,如果值低于索引,则不低于索引将包含相同的值。
然后搜索较高的一半,继续直到找到元素,或达到一个元素范围。
答案 5 :(得分:1)
您的线性搜索理念看起来是正确的,是的。我个人想不出另一种找到这个值的方法,除非你按照你想要的值排序的方式总是在第一个元素中。
编辑:好的,我错了。道歉!答案 6 :(得分:1)
这是我提出的一个解决方案,如果有重复的话,它会起作用(我错误地忽略了没有重复的警告)。
//invariant: startIndex <= i <= endIndex
int modifiedBsearch(int startIndex, int endIndex)
{
int sameValueIndex = -1;
int middleIndex = (startIndex + endIndex) /2;
int middleValue = array[middleIndex];
int endValue = array[endIndex];
int startValue = array[startIndex];
if(middleIndex == middleValue)
return middleValue;
else {
if(middleValue <= endIndex)
sameValueIndex = modifiedBsearch(middleIndex + 1, endIndex)
if(sameValueIndex == -1 && startValue <= middleIndex)
sameValueIndex = modifiedBsearch(startIndex, middleIndex -1);
}
return sameValueIndex;
}
我猜这需要O(log n)时间,但乍看之下还不清楚???
如果你运气不好,那就需要O(n log n)时间(堆栈树的高度为log n,它将是一个完整的树,最后一级有n个节点,n / 2倒数第二,等等。
因此,平均而言,它将介于O(log n)和O(n log n)之间。
答案 7 :(得分:0)
是的,我相信你是对的。没有logarathmic搜索是可能的,因为没有办法减少我们拥有的数据。我们需要查看数组中的所有索引。
@Kos,我不知道如何对数组进行排序会有什么不同?
答案 8 :(得分:0)
在我的头脑中,进行二进制分割可能会更快。
查看中间值,如果它高于您需要的值,请在下半部分重新搜索。
经过一次比较后,您已将数据集分成两半
答案 9 :(得分:0)
在阅读问题之后,似乎有一种方案可用于加速查找。将位置与值进行比较时,如果值大于位置,则该值可用作下一个要评估的位置。这将有助于更快地跳过阵列。这可以完成,因为数组已排序。我们跳过的值在概念上被移位到数组的左侧,并且位于错误的位置。
示例:
int ABC[] = { -2, -5, 4, 7, 11, 22, 55 };
如果我当前的位置是2并且它的值为4,则它们不相等,从概念上讲,值4向左移动。我可以使用4的值作为我的下一个位置,因为如果值4超出位置,则小于4的所有值也都不在位置。
一些示例代码只是为了讨论:
void main()
{
int X[] = { -3, -1, 0, 3, 5, 7};
int length = sizeof(X)/sizeof(X[0]);
for (int i = 0; i < length;) {
if (X[i] > i && X[i] < length)
i = X[i]; // Jump forward!
else if (X[i] == i) {
printf("found it %i", i);
break;
} else
++i;
}
}
答案 10 :(得分:0)
二进制搜索的修改版本就足够了我猜
假设序列是
Array : -1 1 4 5 6
Index : 0 1 2 3 4
Result : 1
或
Array : -2 0 1 2 4 6 10
Index : 0 1 2 3 4 5 6
Result: 4
从这两个例子中我们看到,如果mid&lt;中,那么所需的结果将永远不会出现在右侧。 a [mid] ...伪代码看起来像这样
mid <- (first + last )/2
if a[mid] == mid then
return mid
else if a[mid] < mid then
recursive call (a,mid+1,last)
else
recursive call (a,first,mid-1)
答案 11 :(得分:0)
爪哇:
public static boolean check (int [] array, int i)
{
if (i < 0 || i >= array.length)
return false;
return (array[i] == i);
}
C ++:
bool check (int array[], int array_size, int i)
{
if (i < 0 || i >= array_size)
return false;
return (array[i] == i);
}