1 题目
题目:在大数组中查找(Search in a Big Sorted Array)
描述:给一个按照升序排序的非负整数数组。这个数组很大以至于你只能通过固定的接口 ArrayReader.get(k) 来访问第k个数(或者C++里是ArrayReader->get(k)),并且你也没有办法得知这个数组有多大。找到给出的整数target第一次出现的位置。你的算法需要在O(logk)的时间复杂度内完成,k为target第一次出现的位置的下标。如果找不到target,返回-1。
- 如果你访问了一个不可访问的下标(比如越界),ArrayReader 会返回2,147,483,647。
lintcode:题号——447,难度——medium
样例1:
输入: [1, 3, 6, 9, 21, ...], target = 3
输出: 1
样例2:
输入: [1, 3, 6, 9, 21, ...], target = 4
输出: -1
2 解决方案
2.1 思路
由于无法知道数组的长度,所以需要自己制定,因为时间复杂度要求时O(log k),二分的时间复杂度是log的数量级,考虑将二分逆向使用,通过倍增的方式来不断寻找符合条件的边界,找到边界之后即可按照标准二分搜索来解题。
倍增思想在很多容器中被使用到,c++中的vector等主要容器起始分配的容量大小都是一定值的(为了不浪费资源),每次在存入的数据即将超出容量的时候,会动态地将容量加倍。
2.2 时间复杂度
先寻找边界耗时O(log n),再搜索目标耗时O(log n),该题的算法的时间复杂度为O(2*log n) = O(log n)。
深度优先搜索的时间复杂度计算没有通用的方式,只能根据具体题目计算,可以理解成看作答案个数与构造每个答案花费的时间的乘积。
2.3 空间复杂度
使用了vector数据结构保存节点,算法的空间复杂度为O(n)。
3 源码
细节:
- 边界未知,需要先用倍增的方式找到边界。
- 有了边界之后就可以使用二分搜索来找到目标。
C++版本:
/**
* @param reader: An instance of ArrayReader.
* @param target: An integer
* @return: An integer which is the first index of target.
*/
int searchBigSortedArray(ArrayReader * reader, int target) {
// write your code here
int index = 1;
// 倍增寻找边界
while (reader->get(index) < target)
{
index *= 2;
}
// 找到边界之后,二分搜索目标
int start = index / 2;
int end = index;
while (start + 1 < end)
{
int mid = start + (end - start) / 2;
if (reader->get(mid) < target)
{
start = mid;
}
if (reader->get(mid) >= target)
{
end = mid;
}
}
if (reader->get(start) == target)
{
return start;
}
if (reader->get(end) == target)
{
return end;
}
return -1;
}