LeetCode之道——有序数组中的搜索问题

斩题术

有序数组中的搜索问题,首先想到 二分法 解决。

下面以剑指offer中的相关题目进行实践,理论结合实际。


剑指offer 53-I. 在排序数组中查找数字


题目描述:
统计一个数字在排序数组中出现的次数。

示例1

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

利用二分法思想解题

对于有序数组,统计其中一个数字出现的次数,只要找到这个数字的左右边界即可。
而左右边界的查找可以采用二分法,以右边界为例。

给定数组 [5,7,7,8,8,10],target = 8 ,i = 0,j = 5(最后一个元素的下标)。
二分法: m = (i + j) / 2; 因此m = 2,nums[2] = 7,小于8,因此target应在区间[m+1, j]之间。
更新左边界,令i = m+1;(i 变为 3)
继续二分法:m = (i + j) / 2; 因此m = 4,nums[4] = 8,等于8,但这个8不一定是右边界,继续在[m+1, j]间寻找右边界。
更新左边界,令i = m+1;(i 变为 5)
继续二分法:m = (i + j) / 2; 因此m = 5,nums[5] = 10,大于8,说明原来的m就是右边界了,令j = m - 1; 这个 j 就是右边界。

左边界同理,只需将target - 1即可,因为相当于找的是离target最近的、比target小的某个数的右边界。

这里有一个数学小细节,对于本例数组3、4位置上的数字,它们都是8。
但4-3是1,因此若右边界是下标为4,左边界应是下标为2,4-2=2。
数字8出现了2次,这才正确。

helper函数就是进行循环二分的函数,进行左右边界的查找。

class Solution {
    public int search(int[] nums, int target) {
        return helper(nums, target) - helper(nums, target - 1);
    }
    int helper(int[] nums, int tar) {
        int i = 0, j = nums.length - 1;
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] <= tar) i = m + 1;
            else j = m - 1;
        }
        return i;
    }
}

复杂度分析

时间复杂度:O(logN)
空间复杂度:O(1)


剑指offer 53-II. 0~n-1中缺失的数字


题目描述:
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例1

输入: [0,1,2,3,4,5,6,7,9]
输出: 8

利用二分法思想解题

前面是统计长度,这里是找缺失的值,但归根到底,还是找右边界

为什么这么说呢?看我分析。
可以将数组看作两部分,一部分是连续有序序列,直到碰到缺省的那个值;另一部分是缺省值后的连续有序序列。
前一部分特点:nums[m] = m;
后一部分特点:nums[m] ≠ m;
因此缺失的数字就是后一部分序列的首位元素的索引,也就是前一部分的右边界。

具体例子略,直接看代码。

class Solution {
    public int missingNumber(int[] nums) {
        int i = 0, j = nums.length - 1;
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] == m) {
                i = m + 1;
            }else {
                j = m - 1;
            }
        }
        return i;
    }
}

复杂度分析

时间复杂度:O(logN)
空间复杂度:O(1)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值