剑指Offer53-Ⅰ—在排序数组中查找数字

剑指Offer53-Ⅰ

题意

统计一个数字在排序数组中出现的次数。

 

解法1—暴力循环

class Solution 
{
public:
    int search(vector<int>& nums, int target) 
    {
        int count=0;
        for(auto& num:nums)
        {
            if(num==target)
                count++;
        }
        return count;
    }
};

解法2—哈希表

class Solution 
{
public:
    int search(vector<int>& nums, int target) 
    {
        unordered_map<int,int> umap;
        for(auto& num:nums)
        {
            umap[num]++;
        }
        return umap[target];
    }
};

 解法3—二分法

由题目可知,nums是一个非递减数组。排序数组中的搜索问题,首先想到 二分法 解决。

排序数组 nums 中的 所有target数字 形成一个窗口,记窗口的 左 / 右边界 索引分别为 left 和 right ,分别对应窗口左边 / 右边的首个元素。

本题要求统计数字 target 的出现次数,可转化为:使用二分法分别找到 左边界 left 和 右边界 right ,易得数字 target 的数量为 right - left - 1。


这样的话呢,我们会把算法流程分为两部分,第一部分是查找左边界,第二部分是查找右边界,有没有办法将二者统一起来呢?

假设定义我们的二分查找函数是 f,当它的输入参数是x的时候,我们令 f(x)返回结果为x在有序数组nums中插入的位置,如果 f(x) 返回的结果是i的话,这就说明在nums的[0,i-1]闭区间的范围内的所有元素统统是 小于等于x 的;而在nums的[i+1,n-1]闭区间内所有的数字统统是大于x的。

例如:

 f(7) = 3。即target形成的窗口的第一个位置。

f(8) = 5。即target形成的窗口的最后一个位置的下一个位置。

这样,直接返回 f(8)-f(7)就可以得到最终的答案了。

f函数算法解析:

  1. 初始化: 左边界 L = 0,右边界 R = len(nums) - 1。
  2. 循环二分:当L大于R时跳出循环
    1. 计算中点 m = L+(R-L)/2 (向下取整);
    2. 若 nums[m] ≤  target ,中点值比target小或者等于它,target插入位置肯定在[m+1,R] 闭区间了,因此执行 L = m + 1;

    3. 若 nums[m] > target,中点值比target大,target插入位置肯定在中点之前了,即[L,m-1]闭区间 ,因此执行 R = m - 1;

  3. 返回值: 返回最终的答案L。

C++实现

class Solution 
{
public:
    int f(vector<int>& nums,int x)
    {
        //寻找x的右边界
        //本质上来说就是查找数字x在Nums中的插入位置
        //若数组中存在值相同的元素,则插入位置为这些元素的右边
        
        int L=0,R=nums.size()-1;
         //当左右边界(L和R)指向同一个位置的时候,并没有办法判断 m 位置处的元素是≤x还是>x
         //因为我们最终返回的是L,而如果说 nums[m]≤x,L是需要在原有基础上加一的
         //否则,若nums[m]>x,L不会被改变,应该改变R,使得R<L,跳出循环。
         //所以即使 L等于R的时候,依然要判断 m处位置的元素与x的大小关系。
        while (L<=R)
        {
            int m = L+(R-L)/2;
            if(nums[m]<=x)
                L=m+1;
            else
                R=m-1;
        }
        return L;

    }
    int search(vector<int>& nums, int target) 
    {
        //去找 (target-1) 的右边界,实际上就是查找target的左边界所在位置
        //主要是找位置,而不是找数字存不存在,target-1的目标是找到离target的左窗口最近的那个位置点。
        return f(nums,target)-f(nums,target-1);
    }
};

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心之所向便是光v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值