Magic Index:如果一个数组A
中的索引i
满足A[i] = i
,则i
就是一个魔术索引。给定一个有序数组,查找其中最小的魔术索引,不存在则返回-1
。
最简单的方法就是对数据进行一次遍历,这样时间复杂度为O(n)
,注意到数组是有序的,所以有可能实现一个O(logn)
的算法。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 2 | 2 | 3 | 4 | 7 | 9 | 12 | 13 |
在上面的数组中,A[5] = 3
,用idx
表示索引,elt
表示索引对应的元素:
- 如果数组中没有重复元素,那么索引不可能在左边,因为索引减少
k
时,元素值至少也减少k
,所以左边会一直保持idx > elt
;同理,如果索引值小于元素值,则右边的idx < elt
- 如果数组中有重复元素,在索引值大于元素值时,左边也可能存在魔术索引,但是魔术索引的上界是
3
;同理,如果索引值小于元素值右边也可能存在魔术索引,但是魔术索引的下限为5 + 1
注意书上的代码只返回一个就好,所以可以使用二分法,而力扣要求返回索引最小值,所以二分法不一定肯定可以排除一半的。同时力扣上的测试用例有问题,例如下面的用例应该都返回0
,而力扣都返回1
。
[0, 1, 1]
[0, 1, 2, 2, 2, 3, 4, 7, 9, 12, 13]
class Solution {
public:
int findMagicIndex(vector<int>& nums) {
return MagicIndex(nums, 0, nums.size());
}
private:
int MagicIndex(const vector<int> &nums, int b, int e)
{
int mid = b + (e - b) / 2, ret;
if(b >= e) return -1;
//int end = mid <= nums[mid] ? mid : mid;
ret = MagicIndex(nums, b, mid);
if(ret != -1) return ret;
else if(mid == nums[mid]) return mid;
else{
int begin = mid < nums[mid] ? nums[mid] : mid + 1;
return MagicIndex(nums, begin, e);
}
}
};