不会死的二分模板
朋友们在写二分查找的题目的时候,写出的二分是不是经常死循环。
right的值是mid-1还是mid+1?还是就等于mid呢?
重点来了,今天给大家介绍一种不会出现死循环的二分写法
寻找target下界模板
int findPosition(vector<int> &nums,int target)
{
int left=0;
int right=nums.size()-1;
//结束条件为left和right相邻
while(left+1<right)
{
int mid=left+(right-left)/2;
if(a[mid]==target)
right=mid;
else if(a[mid]<target)
left=mid;
else if(a[mid>target])
right=mid;
}
if(a[left]==target)
return left;
if(a[right]==target)
return right;
return -1;
}
寻找target上界模板
int findPosition(vector<int> &nums,int target)
{
int left=0;
int right=nums.size()-1;
//结束条件为left和right相邻
while(left+1<right)
{
int mid=left+(right-left)/2;
if(a[mid]==target)
left=mid;
else if(a[mid]<target)
left=mid;
else if(a[mid>target])
right=mid;
}
if(a[right]==target)
return right;
if(a[left]==target)
return left;
return -1;
}
题目
模板熟悉了,我们就一起来练习几个经典的二分题目
题目一:457 · 经典二分查找问题 - LintCode
最经典的二分查找的题目。
class Solution {
public:
int findPosition(vector<int> &nums, int target) {
// write your code here
if(nums.size()==0)
return -1;
int left=0;
int right=nums.size()-1;
while(left+1<right)
{
int mid=left+(right-left)/2;
if(nums[mid]==target)
{
right=mid;
}
else if(nums[mid]<target)
{
left=mid;
}
else if(nums[mid]>target)
{
right=mid;
}
}
if(nums[left]==target)
return left;
if(nums[right]==target)
return right;
return -1;
}
};
题目二:458 · 目标最后位置 - LintCode)
class Solution {
public:
/**
* @param nums: An integer array sorted in ascending order
* @param target: An integer
* @return: An integer
*/
int lastPosition(vector<int> &nums, int target) {
// write your code here
int left=0;
int right=nums.size()-1;
if(nums.size()==0)
return -1;
while(left+1<right)
{
int mid=left+(right-left)/2;
if(nums[mid]==target)
left=mid;
else if(nums[mid]<target)
left=mid;
else if(nums[mid]>target)
right=mid;
}
if(nums[right]==target)
return right;
if(nums[left]==target)
return left;
return -1;
}
};
题目三:65 · 两个排序数组的中位数 - LintCode
思路
对于一个长度为
n
的排序数组a
,若n
为奇数,中位数为a[(n/2+1)]
,若n
为偶数,则中位数为(a[n/2]+a[n/2+1])/2
,如果我们可以在两个数列中求出第K
小的元素,便可以解决该问题。
不妨设数列A元素个数为n
,数列B元素个数为m
,各自升序排序,求第k
小元素。
取A[k/2]
与B[k/2]
比较,如果A[k/2] > B[k/2]
,那么,所求的元素必然不在B的前k/2
个元素中(证明反证法)。反之,必然不在A的前k/2
个元素中,于是我们可以将A或B数列的前k/2
元素删去,求剩下两个数列的k-k/2
小元素,于是得到了数据规模变小的同类问题,递归解决。
如果k/2
大于某数列个数,所求元素必然不在另一数列的前k/2
个元素中,同上操作就好。
class Solution {
public:
/**
* @param a: An integer array
* @param b: An integer array
* @return: a double whose format is *.5 or *.0
*/
double found(vector<int> &a, vector<int> &b,int starta,int startb,int k)
{
//如果A数组里面没有数
if(starta>=a.size())
return b[startb+k-1];
//如果B数组里面没有数
if(startb>=b.size())
return a[starta+k-1];
//递归的退出条件,如果找最小的数就找两个数组第一个数的较小数
if(k==1)
return min(a[starta],b[startb]);
//如果一个数组太短,那么删除的一定是长的数组的前k/2
//所以如果数组长度不够,比较值设置为INT_MAX,删除的就是另一个数组的数
int keya=starta+k/2-1>=a.size()?INT_MAX:a[starta+k/2-1];
int keyb=startb+k/2-1>=b.size()?INT_MAX:b[startb+k/2-1];
if(keya>keyb)
return found(a,b,starta,startb+k/2,k-k/2);
else
return found(a,b,starta+k/2,startb,k-k/2);
}
double findMedianSortedArrays(vector<int> &a, vector<int> &b) {
// write your code here
int sum=a.size()+b.size();
double ret;
if(sum&1)
ret=found(a,b,0,0,sum/2+1);
else
ret=(found(a,b,0,0,sum/2)+found(a,b,0,0,sum/2+1))/2;
return ret;
}
};
题目四:28 · 搜索二维矩阵 - LintCode
思路:
可以看作是一个有序数组被分成了n段,每段就是一行。因此依然可以二分求解。
对每个数字,根据其下标i,j进行编号,每个数字可被编号为0~n*n-1相当于是在一个数组中的下标。然后直接像在数组中二分一样来做。取的mid要还原成二位数组中的下标,i = mid/n, j = mid%n
class Solution {
public:
/**
* @param matrix: matrix, a list of lists of integers
* @param target: An integer
* @return: a boolean, indicate whether matrix contains target
*/
bool searchMatrix(vector<vector<int>> &matrix, int target) {
// write your code here
int n=matrix.size();
if(maxtrix==NULL||n==0) return false;
int m=matrix[0].size();
if(maxtrix[0]==NULL||m==0) return false;
//进行二分查找
int left=0;
int right=n*m-1;
while(left+1<right)
{
int mid=left+(right-left)/2;
int row=mid/m;
int col=mid%m;
if(matrix[row][col]==target)
return true;
else if(matrix[row][col]>target)
right=mid;
else if(matrix[row][col]<target)
left=mid;
}
if(matrix[left/m][left%m]==target)
return true;
if(matrix[right/m][right%m]==target)
return true;
return false;
}
};
题目四:60 · 搜索插入位置 - LintCode
class Solution {
public:
/**
* @param a: an integer sorted array
* @param target: an integer to be inserted
* @return: An integer
*/
int searchInsert(vector<int> &a, int target) {
// write your code here
if(a.size()==0)
return 0;
int left=0;
int right=a.size()-1;
while(left+1<right)
{
int mid=left+(right-left)/2;
if(a[mid]==target)
return mid;
else if(a[mid]>target)
right=mid;
else if(a[mid]<target)
left=mid;
}
if(target>a[right])
return right+1;
if(target>a[left])
return right;
return 0;
}
};
题目五:447 · 在大数组中查找 - LintCode
第一步:
由于一开始无法找到右边界,就把右边界下标设为1。不断成倍的扩大可遍历的数组,直到右边界值大于等于target。
第二步:
套模板
class Solution {
public:
int searchBigSortedArray(ArrayReader * reader, int target) {
int left=0,right=1;
while(reader->get(right)<target)
right<<=1;
//在区间内二分查找
while(left+1<right)
{
int mid=left+(right-left)/2;
if(reader->get(mid)<target)
left=mid;
else
right=mid;
}
if(reader->get(left)==target)
return left;
if(reader->get(right)==target)
return right;
return -1;
}
};
题目六:159 · 寻找旋转排序数组中的最小值 - LintCode
此题的难点为:需要通过判断出mid的区间,最后在得出和target的关系
class Solution {
public:
int findMin(vector<int> &nums) {
int left=0;
int right=nums.size()-1;
while(left+1<right)
{
int mid=left+(right-left)/2;
if(nums[mid]>nums[right])
left=mid;
else
right=mid;
}
//相邻时退出
return min(nums[left],nums[right]);
}
};
题目七:62 · 搜索旋转排序数组 - LintCode
这道题是上一道题目的变式。不仅仅要找到mid的区间,还要比较target和mid的关系。
class Solution {
public:
int search(vector<int> &a, int target) {
int left=0,right=a.size()-1;
if(left>right)
return -1;
while(left+1<right)
{
int mid=left+(right-left)/2;
//如果mid落在了左区间
if(a[mid]>a[left])
{
//如果target在mid的左边并且不再右区间
if(target<a[mid]&&a[left]<=target)
right=mid;
else
left=mid;
}
else
{
if(target>a[mid]&&target<=a[right])
left=mid;
else
right=mid;
}
}
if(a[left]==target)
return left;
if(a[right]==target)
return right;
return -1;
}
};
题目八:183 · 木材加工 - LintCode
思路
基于答案值域的二分法。木头的长度范围为(0,max(L)),取一个中间的长度(left+right)/2;计算该长度是否可以分为K根,如果多于K,就变长一点。如果少于K,就变短一点。
class Solution {
public:
//判断是否长度是否合适
bool cut(vector<int>&l,int len,int k)
{
int count=0;
for(int i=0;i<l.size();i++)
{
count+=l[i]/len;
}
if(count>=k)
return true;
else
return false;
}
int woodCut(vector<int> &l, int k) {
if(l.size()==0)
return 0;
int maxl=0;
for(int i=0;i<l.size();i++)
{
maxl=max(maxl,l[i]);
}
int start=0,end=maxl;
while(start+1<end)
{
int mid=start+(end-start)/2;
if(cut(l,mid,k))
{
start=mid;
}
else
end=mid;
}
if(cut(l,end,k)) return end;
return start;
}
};