【路飞】搜索旋转排序数组ii

126 阅读3分钟

记录 1 道算法题

搜索旋转排序数组ii

leetcode-cn.com/problems/se…


要求:提供一个数组和一个数,数组是由一个升序的数组,在某个位置切成两半,然后交换位置合成的。如果这个数组里有这个数,返回 true,没有就返回 false。

比如:nums = [2,5,6,0,0,1,2], target = 0 输出 true。

已知数组是由两段升序的数组组成,所以我们可以知道数组的第一个位置的数一定大于等于数组最后一个数。但不知道的是这两段数组的分界在哪。

所以在使用二分的时候会遇到几种情况,中间的点在左边的数组,或者在右边的数组。然后目标数字也可能在左边的数组或者在右边的数组。

所以在划分区间的时候,首先第一种分支。

判断中间的点是在左边的数组还是右边的数组,如果比数组的第一个位置的数大,那么就是在左边的数组。因为在左边的数组的每一个元素都一定大于右边数组的元素。

当位于左边的数组的时候,再判断是在目标的左边或者右边,然后收缩区间。当中间的数大于目标的时候,有两种情况,目标在中间的数的左边,或者目标在右边的数组。所以如果同时目标比左边的数组的第一个元素大,那么就说明目标在左边。此时收缩右区间,反之收缩左区间。

    上面描述的情况是 [5,6,7,8,1,2,3,4] 目标:7,起始区间 [0,7]
    中间的数 8,区间收缩至 [0,2]

然后就是第二种分支,就是中间的数在右边的数组。

如果目标比中间的数大,那么可能目标在中间的数的右边或者目标在左边的数组。如果同时目标比数组的最右边的数小,那么就可以确定目标在右边的数组,收缩左区间。反之收缩右区间。

最后还有一种情况要考虑,当数组里面的数都相等的情况下,左边和右边的数会有相同的部分和中间的数相等,这时不知道应该收缩哪个区间,所以这时候两边都同时收缩。

比如: [1,1,1,1,1,1,0,1]

    function search(nums, target) {
        let l = 0
        let r = nums.length - 1
        while(l <= r) {
            // 算中间的下标,向下取整
            const mid = (l + r) >> 1
            const m = nums[m]
            // 如果刚好是中间
            if (m === target) return true
            // 如果两边的数都和中间的数相等
            if (nums[l] === m && nums[r] === m) {
                l++
                r--
            } else if (m >= nums[l]) {
                // m 在左边的升序区间 同时 目标也在左边的升序区间
                if (m >= target && target >= nums[l]) {
                    r = mid - 1
                } else {
                    l = mid + 1
                }
            } else {
                // m 在右边的升序区间 同时 目标也在右边的升序区间
                if (target <= nums[nums.length - 1] && m <= target) {
                    l = mid + 1
                } else {
                    r = mid - 1
                }
            }
        }
        
        return false
    }