五月集训(第09天)—二分查找
二分查找
1. 35. 搜索插入位置
思路:
对每个数平方后排序即可
class Solution {
public:
bool check(int ans, int target) {
if (ans >= target) return true;
return false;
}
int Binary_Search(int target, vector<int>& nums) {
int l = 0, r = nums.size() - 1, mid;
int ans = r + 1; // 初值考虑边界情况
while (l <= r) {
mid = l + (( r - l) >> 1);
if (check(nums[mid], target)) {
// 找到目标值下标ans,或大于目标值的第一个下标
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
//cout << "ans = "<< mid << endl;
}
return ans;
}
int searchInsert(vector<int>& nums, int target) {
return Binary_Search(target, nums);
}
};
没想到啊没想到,一道模板题裂开了这么多发,原来是没有考虑查找的边界情况,记住了。
2. 704. 二分查找
思路:
找等于target
的下标或者大于target
的第一个下标。判断如果没找到,则返回-1
,否则返回结果。
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = nums.size();
int l = 0, r = n - 1, mid, ans = n - 1;
while (l <= r) {
mid = l + ((r - l) >> 1);
if (nums[mid] >= target) {
// 等于target或者大于的第一个下标
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
return (nums[ans] != target) ? -1 : ans;
}
};
3. 剑指 Offer 53 - I. 在排序数组中查找数字 I
思路:
思路(1):是找到目标值,然后向后遍历,如果是整个数组都是target
的极端情况下,时间复杂度为$O(n)$。
思路(2):是利用两套二分方案,分别找到目标值的最左端和最右端,时间复杂度是$O(logn)$不过代码有点长
思路(3):看到英雄哥的思路,妙啊,由于我的check
方式获取的是目标值下标或者大于目标值的第一个下标,所以直接找target
和target+1
的下标做差即可,时间复杂度$O(logn)$。
class Solution {
public:
int Binary_Search(int target, int n, vector<int> &nums) {
int l = 0, r = n - 1, mid, ans = n;
while (l <= r) {
mid = l + ((r - l) >> 1);
if (nums[mid] >= target) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
return ans;
}
int search(vector<int>& nums, int target) {
int n = nums.size();
int ans = Binary_Search(target, n, nums);
return (ans == n || nums[ans] != target) ? 0 : Binary_Search(target + 1, n, nums) - ans;
}
};
4. 911. 在线选举
题目理解补充:
[0,1,1,0,0,1,0], [0,5,10,15,20,25,30]
上面的输入表示:
第 0 分钟投给 0 号; 第 5 分钟投给 1 号; 第 10 分钟投给 1 号; 第 15 分钟投给 0 号; 第 20 分钟投给 0 号; 第 25 分钟投给 1 号; 第 30 分钟投给 0 号;
思路:
step1: 预处理出每个时刻内的winner
step2: 二分查找t时刻对应的下标,索引得到winner
#define maxn 5001
typedef struct {
int p[maxn], t[maxn], win[maxn];
int cnt[maxn];
int size;
} TopVotedCandidate;
// 预处理每个时刻内的winner
TopVotedCandidate* topVotedCandidateCreate(int* persons, int personsSize, int* times, int timesSize) {
// 申请空间
TopVotedCandidate *obj = (TopVotedCandidate *)malloc(sizeof(TopVotedCandidate));
// 初始化数组
memset(obj->cnt, 0, sizeof(obj->cnt));
int preMaxPerson = -1;
obj->size = personsSize;
for (int i = 0; i < personsSize; i++) {
obj->p[i] = persons[i];
obj->t[i] = times[i];
obj->cnt[ obj->p[i] ]++;
if (preMaxPerson == -1 || obj->cnt[preMaxPerson] <= obj->cnt[ obj->p[i] ]) {
preMaxPerson = obj->p[i];
}
obj->win[i] = preMaxPerson;
}
return obj;
}
// 二分查找时间t对应下标
int topVotedCandidateQ(TopVotedCandidate* obj, int t) {
int l = 0, r = obj->size - 1, mid, ans = 0;
while (l <= r) {
mid = l + ((r - l) >> 1);
if (obj->t[mid] <= t) {
// 找到时间t的下标或者小于t的第一个下标
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return obj->win[ans];
}
// 释放申请的空间
void topVotedCandidateFree(TopVotedCandidate* obj) {
free(obj);
}
/**
* Your TopVotedCandidate struct will be instantiated and called as such:
* TopVotedCandidate* obj = topVotedCandidateCreate(persons, personsSize, times, timesSize);
* int param_1 = topVotedCandidateQ(obj, t);
* topVotedCandidateFree(obj);
*/
5. 1552. 两球之间的磁力
思路:
经典二分,一个二分框架,一个check()
函数。寻找最小磁力的最大值,二分答案,就要直接找答案,于是直接先二分一个最小磁力的最大值,然后check
在这个值的情况下,能否放下所有的m
个球,如果可以,再去找更大的值,如果不可以,去找小一些的值。
class Solution {
public:
bool check(int ans, int n, int m, vector<int> &position) {
int pre = position[0];
m--;
for (int i = 1; i < n; i++) {
if (!m) return true;
if (position[i] - pre >= ans) {
--m;
pre = position[i];
}
if (!m) return true;
}
return false;
}
int maxDistance(vector<int>& position, int m) {
int n = position.size();
// 二分模板
int l = 0, r = 1e9 + 5, mid, ans;
sort(position.begin(), position.end());
while (l <= r) {
mid = l + ((r - l) >> 1);
if (check(mid, n, m, position)) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}
};