由于今天是周未,所以打算来一道比较有挑战性的题目。属于二分查找类型的,leetcode-33题-搜索旋转排序数组。
先来看下二分查找的定义:二分查找是一种在数组中查找数据的算法,但它只能查找排好序的数据,先找出中间值,将目标值与中间值对比,即可得知目标值是在中间值左边还是右边,从而可以排除一半的元素,提升效率,然后逐层对比,直到找到目标值。先来简单的题实践一下,从数组0到9中找到目标6。
function find(nums, target) {
let left = 0,right = nums.length - 1;
while (left <= right) {
let mid = (left + right) >> 1; // 通过位运算符计算中间值
if (nums[mid] === target) return mid;
else if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
}
示例
find([0,1,2,3,4,5,6,7,8,9], 6)
find([0,1,3,4,6,7,8,9], 6)
在这种题中,如果使用循环遍历查找,时间复杂度是O(n);但如果用二分查找,时间复杂度只是O(log n)。当数据量越大,查找速度快的越明显。下面回到第33题,先看题目:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
一个原本是有序的数组,被在某个未知的点旋转了,变成了半有序的状态,然后在这数组中找到目标值。第一种解法:暴力求解,直接对数组进行循环遍历找目标值,但这种解法时间复杂度是为O(n)的,而题目要求是O(log n)级别,所以暴力求解在这题不能用。第二种解法:虽然已经不是完整的有序数组了,其中一部分是有序排序,还是可以使用二分查找来解决,只是要做多一点判断。
在某个点被旋转了,说明有一部分是有序的,可以进行以下分析:找出有序排序在哪一边,然后通过对比,判断目标值是否在有序排序内。如果在,可以对有序排序内进行二分查找;如果不在,则对另一边进行二分查找。通过分析后,再来看下代码:
var search = function(nums, target) {
let l = 0, r = nums.length - 1;
while (l <= r) {
const mid = (l + r) >> 1
if (nums[mid] === target) return mid
if (nums[mid] >= nums[l]) {
if (nums[mid] > target && target >= nums[l]) {
r = mid - 1
} else {
l = mid + 1
}
} else {
if (target <= nums[r] && target >= nums[mid]) {
l = mid + 1
} else {
r = mid - 1
}
}
}
return -1
};
代码内容大致是和上面那题差不多的,只是多了几层的判断条件。也是通过不断拆两份,一半一半地排除多余元素,直到找到目标值,最后实在找不到则返回-1。这道题难点在于如何判断目标值在哪一边,能理清判断条件,就不难了。
今天内容就到这,早点休息,明天继续搬砖。