剑指Offer-53:在排序数组中查找数字

题目一:数字在排序数组中出现的次数

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

例子:

如,输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4。

链接:

剑指Offer(第2版):P263

思路标签:

  • 算法:二分查找
  • 数组排序好,第一个3和最后一个3决定

解答:

时间复杂度O(logn)的解法
  • 在排序中的数组做查找,很自然可以想到使用二分查找法;
  • 因为数组的个数只取决于第一个和最后一个的位置,所以我们只需要寻找第一个和最后一个该数字的位置即可;
  • 应用二分查找,如果当前的中间数是该数字k,那么要么这个数字就是中间数字,要么就是第一或者最后一个数字;
  • 以查找第一个k为例:
    • 如果中间数字即为k:如果前面一个数字不是k,那么这个k即为第一个;如果前面一个数字也是k,那么第一个k就在数组的前半部分,继续查找。
    • 如果中间数字不为k,大于k则在前半部分查找,小于k则在后半部分查找。
class Solution {
public:
    int GetNumberOfK(vector<int> data, int k) {
        int number = 0;
        int length = data.size();
        if (length > 0) {
            int first = GetFirstK(data, k, 0, length - 1);
            int last = GetLastK(data, k, 0, length - 1);

            if (first > -1 && last > -1)
                number = last - first + 1;
        }

        return number;
    }

    int GetFirstK(vector<int> &data, int k, int start, int end) {
        int length = data.size();
        if (start > end)
            return -1;

        int middleIndex = (start + end) / 2;
        int middleData = data[middleIndex];

        if (middleData == k) {
            if ((middleIndex > 0 && data[middleIndex - 1] != k) || middleIndex == 0)
                return middleIndex;
            else
                end = middleIndex - 1;
        }
        else if (middleData > k)
            end = middleIndex - 1;
        else
            start = middleIndex + 1;

        return GetFirstK(data, k, start, end);
    }

    int GetLastK(vector<int> &data, int k, int start, int end) {
        int length = data.size();
        if (start > end)
            return -1;

        int middleIndex = (start + end) / 2;
        int middleData = data[middleIndex];

        if (middleData == k) {
            if ((middleIndex < length - 1 && data[middleIndex + 1] != k) || middleIndex == length - 1)
                return middleIndex;
            else
                start = middleIndex + 1;
        }
        else if (middleData < k)
            start = middleIndex + 1;
        else
            end = middleIndex - 1;

        return GetLastK(data, k, start, end);
    }
};

题目二:0~n-1中缺失的数字

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

链接:

剑指Offer(第2版):P266

LeetCode-268:Missing Number(寻找缺失数字)

思路标签:

  • 算法:二分查找

解答:

时间复杂度O(logn)的解法
  • 一种比较直观的解法就是利用公式n(n-1)/2对0~n-1求和记为s1,再求数组中所有数字和,记为s2,s1-s2的结果就是确实的数字。时间复杂度O(n),没有利用数组排序的性质。
  • 转换问题,因为是排序数组,数组中前面的数字和其下标是相同的,从缺失数字开始,后面的数字和其下标不相同。
  • 利用上面的性质,使用二分查找:寻找在排序数组中第一个值和下标不相等的下标。
class Solution {
public:
    int GetMissingNumber(const int* numbers, int length) {
        if (numbers == nullptr || length < 0)
            return -1;

        int start = 0;
        int end = length - 1;
        while (start <= end) {
            int middle = (start + end) >> 1;
            if (numbers[middle] != middle) {
                if (middle == 0 || numbers[middle - 1] == middle - 1)
                    return middle;

                end = middle - 1;
            }
            else
                start = middle + 1;
        }

        if (start == length)
            return length;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值