LeetCode 面试题 08.03. 魔术索引
题目
注意点:
- 有序整数数组
- 可能存在多个魔术索引
- 数组可能包含重复元素
解题
解题一:线性搜索 + 剪枝
不过,既然给定数组是有序的,我们理应充分利用这个条件。
// javascript
var findMagicIndex = function(nums) {
for (let i = 0; i < nums.length; i = Math.max(i + 1, nums[i])) {
if (i === nums[i]) return i;
}
return -1;
};
时间复杂度:最坏情况下会达到
O
(
n
)
O(n)
O(n) 的时间复杂度,其中
n
n
n 为数组的长度。
空间复杂度:
O
(
1
)
O(1)
O(1)。
解题二:二分查找 + 剪枝
下面解法适用于:至多有一个魔术索引 & 数组不包含重复元素
下面解法适用于:至多有一个魔术索引 & 数组可能包含重复元素
上面的代码先比较中间元素,再去左半部分搜索,然后去右半部分搜索。假设数组为 [-99, -87, -69, -51, -40, -38, -31, -30, -18, -1, 10, 11, 41, 63, 71, 72, 72, 78, 86, 88],找到的是 index 11, value 11,找到 11 后没有再去找 index 10,导致找到的不是最小魔术索引,因而需要改变一下判断顺序,即:如果左边没有找到魔术索引,再比较中间元素。
// javascript
var findMagicIndex = function(nums) {
return magicFast(nums, 0, nums.length - 1);
};
var magicFast = function(nums, start, end) {
if (start > end) return -1;
// 搜索左半部分
let midIndex = Math.floor((start + end) / 2),
midValue = nums[midIndex];
let leftIndex = Math.min(midIndex - 1, midValue);
let left = magicFast(nums, start, leftIndex);
if (left >= 0) return left;
// 比较中间元素
if (midIndex === midValue) return midIndex;
// 搜索右半部分
let rightIndex = Math.max(midIndex + 1, midValue);
let right = magicFast(nums, rightIndex, end);
return right;
};
时间复杂度:最坏情况下会达到
O
(
n
)
O(n)
O(n) 的时间复杂度,其中
n
n
n 为数组的长度;最好情况下
O
(
l
o
g
n
)
O(logn)
O(logn)。
空间复杂度:递归函数的空间取决于调用的栈深度,而最坏情况下我们会递归
n
n
n 层,即栈深度为
O
(
n
)
O(n)
O(n)。
官方解答:魔术索引