剑指offer之数字在排序数组中出现的次数C++解法

本文探讨了在排序数组中统计特定数字出现次数的三种高效算法。第一种使用一次二分查找定位目标数字,随后在左右两侧进行顺序扫描。第二种采用两次二分查找,分别定位目标数字的第一个和最后一个实例。第三种适用于整数数组,利用二分查找确定目标数字与相邻浮点数之间的插入位置,从而计算其频率。

剑指offer之数字在排序数组中出现的次数C++解法


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

思路:既然是排序数组,作为二分查找的必要条件,应该立马想到二分查找算法。这道题要求统计某个数字在数组中出现的次数,因此二分查找的应用可以采用不同方式:
一、直接用二分查找法找到一个目标数字,然后在该位置前后两边顺序扫描,找出第一个和最后一个目标数字,这种算法的效率和直接从头到尾顺序扫描整个数组统计目标数字个数的方法是一样的;
二、利用二分查找法分别找到第一个和最后一个目标数字,发挥了二分查找法的效率。
三、如果所给数组的元素都是整数,假设目标数字是整数key,那么可以通过二分法查找key-0.5和key+0.5在该数组中的插入位置,所有key都应该位于(key-0.5,key+0.5)这个区间当中,通过这两个数的插入位置可以直接计算出key在数组中的个数。

方法一

利用一次二分查找算法:

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        if(data.empty())
            return 0;
        int mid=biSearch(data,k);
        if(mid<0) //如果没有找到k,此时mid==-1,直接返回0
            return 0;
        int i=mid-1,j=mid+1;
        int count=1;
        while(i>=0&&data[i--]==k) //从mid向左边顺序查找并计数
            count++;
        while(j<=data.size()-1&&data[j++]==k)//从mid向右边查找并计数
            count++;
        return count;
    }
    int biSearch(vector<int> data,int k){
        int begin=0,end=data.size()-1;
        int mid;
        while(begin<=end){
            mid=(begin+end)>>1;
            if(data[mid]==k)
                return mid;
            else if(data[mid]<k)
                begin=mid+1;
            else
                end=mid-1;
        }
        return -1;
    }
};

方法二

利用两次二分查找算法分别找到第一个和最后一个目标数字:

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        if(data.empty())
            return 0;
        int left=GetFirstK(data,k,0,data.size()-1);
        int right=GetLastK(data,k,0,data.size()-1);
        int count=0;
        if(left!=-1&&right!=-1)
            count=right-left+1;
        return count;
    }
    int GetFirstK(vector<int> data,int k,int begin,int end){//用递归方式实现二分查找
        if(begin>end)
            return -1;
        int mid=(begin+end)>>1;
        if(data[mid]==k){
            if((mid>0&&data[mid-1]!=k)||mid==0)
                return mid;
            else
                end=mid-1;
        }
        else if(data[mid]>k)
            end=mid-1;
        else
            begin=mid+1;
        return GetFirstK(data,k,begin,end);
    }
   /* 
   int GetFirstK(vector<int> data,int k,int begin,int end){//另外一种写法的递归方式实现二分查找
        if(begin>end)
            return 0;
        int mid=(begin+end)>>1;
        if(data[mid]>k)
            return GetFirstK(data,k,begin,mid-1);
        else if(data[mid]<k)
            return GetFirstK(data,k,mid+1,end);
        else if(mid>0&&data[mid-1]==k)
            return GetFirstK(data,k,begin,mid-1);
        else
            return mid;
    }   
	*/
    int GetLastK(vector<int> data,int k,int begin,int end){//用循环方式实现二分查找
        int mid;
        while(begin<=end){
            mid=(begin+end)>>1;
            if(data[mid]>k)
                end=mid-1;
            else if(data[mid]<k)
                begin=mid+1;
            else if(mid<data.size()-1&&data[mid+1]==k)
                begin=mid+1;
            else
                return mid;
        }
        return -1;
    }
};

方法三

在数组元素均为整数的情况下可以利用这种方法。利用二分查找法查找目标数字k前后两个浮点数的插入位置,即k-0.5, k+0.5在数组中的插入位置,这两个浮点数中间即为所有的k。

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        if(data.empty())
            return 0;
        return biSearch(data,k+0.5)-biSearch(data,k-0.5);
    }
    int biSearch(vector<int> data, double key){
        int begin=0,end=data.size()-1;
        int mid;
        while(begin<=end){
            mid=(begin+end)>>1;
            if(data[mid]>key)
                end=mid-1;
            else
                begin=mid+1;
        }
        return begin; //begin==end时结束循环,因此该条语句等价于return end;
    }
};

查找k-0.5的插入位置时,返回的结果是第一个k的前一个元素的下标,查找k+0.5的插入位置时,返回的结果是最后一个k的下标,因此两个下标直接相减即为k的个数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值