📖 前言
在这个笔记中,我们将介绍二分法这种基本的算法思想,以及它在 C++ 中的应用。我们将从一个小游戏猜数字开始,通过这个案例来引出二分法的概念。然后我们将详细讲解什么是二分法以及它的套路和应用。最后,我们还会介绍 C++ STL 中的二分查找函数。让我们一起来探索吧!
🎯 什么是二分法
二分法是一种高效的搜索算法,通过将搜索范围不断缩小一半来快速找到目标值。它适用于有序数组或有序区间中查找特定元素的问题。二分法的基本思想是通过比较中间值与目标值的大小关系,来确定目标值可能存在的那一半区间,然后在该区间内继续查找。
🎮 小游戏猜数字
让我们通过一个小游戏来理解二分法的基本思想。假设有一个整数范围为 1 到 100,你需要猜一个隐藏的数字。每次猜测后,系统会告诉你猜的数字是大了还是小了,直到猜中为止。
这个小游戏的思路就是使用二分法来快速定位目标数字的范围。每次猜测时,将当前范围的中间数字与目标数字进行比较,根据比较结果来调整范围。通过不断缩小范围,最终可以找到目标数字。
🌟 二分法的套路
二分法有一些常见的套路和应用场景,我们将逐个介绍它们。
3.1 整数的二分
整数的二分是二分法的一种常见应用。我们以两个案例来说明整数的二分应用。
3.1.1 案例1: 力扣704. 二分查找
给定一个升序排列的整数数组 nums
和一个目标值 target
,请你实现二分查找算法,如果目标值存在于数组中,则返回其索引,否则返回 -1。
示例代码:
#include <iostream>
#include <vector>
using namespace std;int binarySearch(vector<int>& nums, int target) {int left = 0;int right = nums.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] == target) {return mid;} else if (nums[mid] < target) {left = mid + 1;} else {right = mid - 1;}}return -1;
}int main() {vector<int> nums = {1, 3, 5, 7, 9, 11};int target = 7;int result = binarySearch(nums, target);if (result != -1) {cout << "目标值 " << target << " 在数组中的索引为 " << result << endl;} else {cout << "目标值 " << target << " 不存在于数组中" << endl;}return 0;
}
运行结果:
目标值 7 在数组中的索引为 3
在上述代码中,我们使用二分法实现了查找目标值在有序数组中的索引。通过不断调整搜索范围的左右边界,并根据中间值与目标值的比较结果来确定下一步的搜索方向,最终可以找到目标值的索引。
3.1.2 案例2: 力扣350. 两个数组的交集 II
给定两个整数数组 nums1
和 nums2
,请你返回它们的交集。结果中每个元素的出现次数应与元素在两个数组中出现的次数一致(如果次数不一致,则取较小的次数)。结果不考虑顺序。
示例代码:
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {unordered_map<int, int> counter;for (int num : nums1) {counter[num]++;}vector<int> result;for (int num : nums2) {if (counter[num] > 0) {result.push_back(num);counter[num]--;}}return result;
}int main() {vector<int> nums1 = {1, 2, 2, 1};vector<int> nums2 = {2, 2};vector<int> result = intersect(nums1, nums2);cout << "两个数组的交集为:";for (int num : result) {cout << num << " ";}cout << endl;return 0;
}
运行结果:
两个数组的交集为:2 2
在上述代码中,我们使用哈希表记录了 nums1
中每个元素的出现次数,并通过遍历 nums2
找到与之对应的交集元素。通过减少交集元素的出现次数,最终可以得到交集的结果。
3.2 实数的二分
除了整数的二分,实数的二分也是二分法的一种应用。在实数的二分中,我们需要考虑精度问题,因为实数在计算机中无法表示为精确的值。下面是一个案例来说明实数的二分应用。
案例: 计算平方根
给定一个非负实数 x
,要
求计算它的平方根并返回。我们可以使用二分法来逼近平方根的值,直到达到所需的精度。
示例代码:
#include <iostream>
using namespace std;double sqrt(double x, double precision) {if (x < 0) {return -1; // 输入错误,返回 -1}double left = 0;double right = x;double mid;while (right - left > precision) {mid = left + (right - left) / 2;if (mid * mid <= x) {left = mid;} else {right = mid;}}return left;
}int main() {double x = 16;double precision = 0.0001;double result = sqrt(x, precision);cout << "输入的数:" << x << endl;cout << "计算得到的平方根:" << result << endl;return 0;
}
运行结果:
输入的数:16
计算得到的平方根:4
在上述代码中,我们通过二分法逼近平方根的值,直到所达到的精度小于指定的 precision
。通过不断调整搜索范围的左右边界,最终可以得到近似的平方根值。
💡 STL 二分查找
除了手动实现二分法算法,C++ STL(标准模板库)中也提供了二分查找的函数。这些函数包括 lower_bound()
和 upper_bound()
,它们可以方便地应用于有序容器中。
4.1 lower_bound()
lower_bound()
函数用于在有序容器中查找第一个大于或等于给定值的元素的迭代器。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int main() {vector<int> nums = {1, 3, 5, 7, 9};int target = 4;auto it = lower_bound(nums.begin(), nums.end(), target);if (it != nums.end()) {cout << "第一个大于或等于目标值的元素为:" << *it << endl;} else {cout << "没有找到满足条件的元素" << endl;}return 0;
}
运行结果:
第一个大于或等于目标值的元素为:5
在上述代码中,我们使用 lower_bound()
函数在有序容器 nums
中查找第一个大于或等于目标值 target
的元素。函数返回一个迭代器,指向满足条件的元素。
4.2 upper_bound()
upper_bound()
函数用于在有序容器中查找第一个大于给定值的元素的迭代器。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int main() {vector<int> nums = {1, 3, 5, 7, 9};int target = 4;auto it = upper_bound(nums.begin(), nums.end(), target);if (it != nums.end()) {cout << "第一个大于目标值的元素为:" << *it << endl;} else {cout << "没有找到满足条件的元素" << endl;}return 0;
}
运行结果:
第一个大于目标值的元素为:5
在上述代码中,我们使用 upper_bound()
函数在有序容器 nums
中查找第一个大于目标值 target
的元素。函数返回一个迭代器,指向满足条件的元素。
📚 总结
在本篇笔记中,我们学习了二分法这种基本的算法思想,并在 C++ 中通过案例和代码进行了详细讲解。我们了解了二分法的套路和应用场景,包括整数的二分和实数的二分。此外,我们还介绍了 C++ STL 中的二分查找函数 lower_bound()
和 upper_bound()
,它们可以方便地应用于有序容器中。希望通过本篇笔记的学习,你对二分法有了更深入的理解,并能够熟练应用于解决问题。祝你在算法学习的道路上越走越远!
本篇笔记内容主要参考了《算法竞赛进阶指南》和 LeetCode 等相关资源。
⭐️希望本篇文章对你有所帮助。
⭐️如果你有任何问题或疑惑,请随时向提问。
⭐️感谢阅读!