二分查找用于在有序数组中查找目标元素,不需要从头遍历到尾,通过对搜索范围折半的方式降低了时间复杂度。
时间复杂度:O(logN)
思路:
由一个左边界和右边界来固定搜索范围,
初始状态下左边界和右边界分别为有序数组的第一个元素和最后一个元素,
将要搜索的元素与搜索范围中的中间元素比较大小,看要搜索的元素在该中间元素的左侧还是右侧,若在左侧,将该中间元素的左侧相邻元素作为新的有边界,反之,将该中间元素的右侧相邻元素作为新的左边界,
重复上述步骤,直到找到目标元素或目标元素不存在(即左边界 > 右边界)。
例子:
在有序数组:1,3,5,7,9,11,13 中查找 5
初始状态:左边界为1,右边界为13,中间元素为7
因为5小于7,所以此时:左边界为1,右边界为5,中间元素为3
因为5大于3,所以此时:左边界为3,右边界为5,中间元素为3
因为5大于3,所以此时:左边界为5,右边界为5,中间元素为5
找到目标元素
代码:
// 返回值为数组下标
int binarySearch(int num[], int numLength, int value) {
int left = 0, right = numLength - 1; // 定义左边界和右边界
while (left <= right) {
int mid = (left + right) / 2;
if (value < num[mid]) {
right = mid - 1; //value在左区间,所以答案在[left, mid-1]
}
else if (value > num[mid]) {
left = mid + 1; //value在右区间,所以答案在[mid+1,right]
}
else { //num[mid]==value,找到目标元素
return mid;
}
}
return 0; //没有找到目标元素
}
例题
思路:
可以切的正方块边长的范围为1~100000,判断每个边长能够切多少块,用块数和人数比较,小于人数则减小边长,大于人数则增大边长,用二分查找的方式来增大或减小边长,直到找到某个边长能够切的块数满足条件。
代码:
#include <iostream>
using namespace std;
int N, K;
int a[100000][2];// 每行第一个元素代表H,第二个元素代表W
// 切边长为x的正方形能切多少块
int cutNum(int a[100000][2], int x) {
int num = 0;
for (int i = 0; i < N; i++) {
num += ((a[i][0] / x) * (a[i][1] / x));// 每个可以切(H/x)*(W/x)块
}
return num;
}
int main()
{
cin >> N >> K;
for (int i = 0; i < N; i++) {
cin >> a[i][0] >> a[i][1];
}
int l = 1, r = 100000;
int mid;
int number;
while (l <= r) {
mid = (l + r) / 2;
number = cutNum(a, mid);
// 如果切的块数小于人数,则减小边长
if (number < K) {
r = mid - 1;
}
// 如果切的块数大于等于人数,则增大边长,若增大后的块数刚好小于人数,则增大前的边长即为答案
else if (number >= K) {
l = mid + 1;
if(cutNum(a,(l + r) / 2) < K){
break;
}
}
}
cout << mid;
return 0;
}