德鲁周记02 --Leetcode二分查找题目类型总结
二分查找
上周因为国庆放假,家人来学校看我所以没有太多时间学习,就把在leetcode上刷到的二分查找的三到类型题和大家分享一下。
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。
算法查找过程很简单,相信大家都玩过猜数的游戏,玩家说猜一个数,游戏会告诉你大了还是小了,根据这个再继续猜数。原理类似,这里就不再介绍了。
但计算机中二分查找必须满足两点要求:
1.必须采用顺序存储结构。
2.必须按关键字大小有序排列。
该算法的时间复杂度是O(log2n)次
同时C++也提供了可以直接调用的库函数
库函数
同时C++也提供了可以直接调用的库函数
头文件: #include
二分查找的函数有 3 个:
lower_bound(起始地址,结束地址,要查找的数值) 返回的是数值 第一个 出现的位置。
upper_bound(起始地址,结束地址,要查找的数值) 返回的是 第一个大于待查找数值 出现的位置。
binary_search(起始地址,结束地址,要查找的数值) 返回的是是否存在这么一个数,是一个bool值。
注意:使用二分查找的前提是数组有序。
1 函数lower_bound()
功能:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置.
注意:如果所有元素都小于val,则返回last的位置,且last的位置是越界的!!
2 函数upper_bound()
功能:函数upper_bound()返回的在前闭后开区间查找的关键字的上界,返回大于val的第一个元素位置
注意:返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置。同样,如果val大于数组中全部元素,返回的是last。(注意:数组下标越界)
PS:
lower_bound(val):返回容器中第一个值【大于或等于】val的元素的iterator位置。
upper_bound(val): 返回容器中第一个值【大于】val的元素的iterator位置。
void main()
{
vector<int> t;
t.push_back(1);
t.push_back(2);
t.push_back(3);
t.push_back(4);
t.push_back(6);
t.push_back(7);
t.push_back(8);
int low=lower_bound(t.begin(),t.end(),5)-t.begin();
int upp=upper_bound(t.begin(),t.end(),5)-t.begin();
cout<<low<<endl;
cout<<upp<<endl;
system("pause");
}
Leetcode题目
二分查找的原理很简单,非常易学,大家如果学过数据结构应该都知道,但是我在Leetcode上碰到包装之后的题目时最开始还是一脸懵逼,是在看了题解以后才明白和理解,所以又刷了两道类型题来加深印象和抓住这种题目特点,今天给大家介绍一下:
1552. 两球之间的磁力
题目链接: https://leetcode-cn.com/problems/magnetic-force-between-two-balls/.
这道题的其实是一个包装了之后的数组问题,可以通过二分查找去找到最后的答案,而不是要你具体去考虑如何分配。
题意求最大化最小,类似这样的求最大化最小值、最小化最大值等都可以用二分查找解决。
通过二分查找check函数来确定是否满足查找标准。题目的难度也就出现在check函数上,二分查找本身其实并不具备任何难度,大家可以看看后面两道类型题,在二分查找部分都是类似的,Check函数上就是区分难度的地方,所以仅仅三道题只是给大家展示一个二分查找的模型和题目难度的区分点,真正的难题应该不限于此,当然太难的题性价比也有待商榷,希望大家可以通过下面代码能够有所收获,后面就不一一讲解了,实在不懂可以通过链接去看题解,题解讲得非常清楚或者留言讨论。
代码
class Solution {
public:
bool check(int x, vector<int>& position, int m) {
int pre = position[0], cnt = 1;
for (int i = 1; i < position.size(); ++i) {
if (position[i] - pre >= x) {
pre = position[i];
cnt += 1;
}
}
return cnt >= m;
}
int maxDistance(vector<int>& position, int m) {
sort(position.begin(), position.end());
int left = 1, right = position.back() - position[0], ans = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid, position, m)) {
ans = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return ans;
}
};
LCP 12. 小张刷题计划
题目链接: https://leetcode-cn.com/problems/xiao-zhang-shua-ti-ji-hua/.
class Solution {
public:
bool Check(int limit, vector<int> cost, int day) {
// 每组划分 limit 的最大和,贪心划分看有多少组
int useday, totaltime, maxtime;
useday = 1; totaltime = maxtime = 0;
for (int i=0; i<cost.size(); ++i) {
int nexttime = min(maxtime, cost[i]);
if (nexttime + totaltime <= limit) {
totaltime += nexttime;
maxtime = max(maxtime, cost[i]);
} else {
++useday;
totaltime = 0;
maxtime = cost[i];
}
}
return (useday <= day);
}
int minTime(vector<int>& time, int m) {
int left, right, middle;
left = right = 0;
for (int i=0; i<time.size(); ++i) {
right += time[i];
}
while (left <= right) {
middle = (left + right) >> 1;
if (Check(middle, time, m)) right = middle - 1;
else left = middle + 1;
}
return left;
}
};
410. 分割数组的最大值
题目链接: https://leetcode-cn.com/problems/split-array-largest-sum/.
class Solution {
public:
bool check(vector<int>& nums, int x, int m) {
long long sum = 0;
int cnt = 1;
for (int i = 0; i < nums.size(); i++) {
if (sum + nums[i] > x) {
cnt++;
sum = nums[i];
} else {
sum += nums[i];
}
}
return cnt <= m;
}
int splitArray(vector<int>& nums, int m) {
long long left = 0, right = 0;
for (int i = 0; i < nums.size(); i++) {
right += nums[i];
if (left < nums[i]) {
left = nums[i];
}
}
while (left < right) {
long long mid = (left + right) >> 1;
if (check(nums, mid, m)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
};
其他题
4.寻找两个正序数组的中位数
这也是一道非常有意思的二分查找的题,和前面的又有所不同,大家可以尝试一下。
下周一定要把数据库的思维导图做出来,已经拖了两周了,希望这个flag别再倒了。