剑指Offer:数字在排序数组中出现的次数
题目:
分析:
在排序数组中查找 → \rightarrow → 二分查找
二分查找
一开始用下图中绿色的那种写法,然后发现找不到左边界(有双下划线的表示进行了比较,可以看出左边界没有被比较过).
所以换成了红色的那种.
反正二分查找记熟就是了.再总结一下伪代码:
if (arr.size() <= 1)
// 特殊处理
int left = 0, right = arr.size(), mid;
while (true) {
mid = (left + right) / 2;
if (arr[mid] == expect) // 找到了. return
if (mid == left) // arr 中没有这个数. return
if (arr[mid] > expect) right = mid;
if (arr[mid] < expect) left = mid;
}
这道题对应的二分查找
如图(图里的 ik 搞错了,应该是 5,但是下面的计算过程用了 4,不过道理都一样):
代码:
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
if (data.empty()) return 0;
if (data.size() == 1) {
if (data[0] == k) return 1;
else return 0;
}
int left = 0, right = data.size(), mid = 0;
while (true) {
mid = (left + right) / 2;
if (data[mid] == k)
return findFirstNotK(data, k, mid, right) - findFirstK(data, k, left, mid);
if (mid == left) return 0; // not found
if (data[mid] > k) right = mid;
if (data[mid] < k) left = mid;
}
}
int findFirstK(vector<int>& data, int k, int left, int right) {
int mid = 0;
while (right - left > 1) {
mid = (left + right) / 2;
if (data[mid] != k) left = mid;
else right = mid;
}
if (data[left] == k) return left;
return right;
}
int findFirstNotK(vector<int>& data, int k, int left, int right) {
int mid = 0;
while (right - left > 1) {
mid = (left + right) / 2;
if (data[mid] != k) right = mid;
else left = mid;
}
return left + 1;
}
};
另外一种解法
由于给出的数组中都是整数,当求 k 的个数时,我们就可以用同一个二分查找函数分别找 k - 0.5 和 k + 0.5 应该出现的位置.
如下图所示,注意左边界的特殊情况.
代码如下:
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
if (data.empty()) return 0;
if (data.size() == 1) {
if (data[0] == k) return 1;
else return 0;
}
return binarySearch(data, k + 0.5) - binarySearch(data, k - 0.5);
}
int binarySearch(vector<int>& data, double d) {
int left = 0, right = data.size(), mid;
while (right - left > 1) {
mid = (left + right) / 2;
if (data[mid] > d) right = mid;
if (data[mid] < d) left = mid;
}
return data[left] < d ? right : left;
}
};