本篇文章适合大致了解“二分查找”到的朋友看,如果不知道二分查找是什么,不适合看此篇文章。
我尽量以通俗易懂的话来说明白二分查找,目的是让大家明白,而不是把它封装成神秘的样子。
虽然文字较多,但你静下心来,仔细看,我相信它会对你有所帮助。
在这里以
· 定义区间范围
· while的循环条件到底如何确定
· 左右区间如何缩小(何时加1,何时不加)
这三个问题展开
1、明确区间定义
二分最开始、最基础的,就是要先【明确区间定义】
什么意思呢?就是说:你想搜索的区间,到底是左闭右闭 [left,right] ,还是左闭右开 [left,right) ,还是左开右开(left,right),还是.......经常用到的是左闭右闭和左闭右开,下面我会以这两个来举例。
(解释以下左闭右闭是什么:闭 就是包含,开就是不包含。左闭,就是包含left,右闭就是包含right。如果是左闭右开,就是包含left,不包含right)
把搜索区间明确好,就要牢记你的区间,不是记它是从哪个下标到哪个下标,而是记住 左闭 还是 左开,右闭 还是 右开
2、while 的循环条件到底如何确定?
判断left < right 或者 left <= right 是不是合法区间。
还记得区间是如何定义的吗,举个例子:【明确区间定义】,我定义的是 [left,right],这个是左闭右闭。重头戏来了:
· 假如写成 while(left <= right),先甭管它是错是对,来判断一下,当 left == right,此时去看定义的区间,因为是 左闭右闭,是包含了 left 和 right 这两个边界的,所以它是一个合法的区间。故在我定义的区间 [left,right] 这个情况下,循环条件就是 while(left <= right)
· 假如写成 while(left < right),也先甭管它是错是对,来判断一下,当 left < right 时,会进入循环吧,但当 left == right 时,就不会进入循环吧,重点来了:我明明定义的是 [left,right],包含了两个边界的,也就是当这两个边界指向同一个下标时,就不会对指向的这个元素进行搜素。但我定义的 [left,right] 是包含了当 left == right 这种情况下,也要进行搜索的呀。所以说不能写成 left < right。
我怕大家还是没明白,再举个例子:这次区间定义成 [left,right),包含左边界,不包含右边界。
· 假如写成 while(left <= right),一样的,判断 left <= right 是不是合法区间。当 left == right 时,会进入循环吧,当 left == right,也就是左边界和右边界都是同一个下标,对于左边界来说是可以的,因为区间定义的时候,本来就包含了左边界。但对于右边边界来说,不包含右边界,那么又不能包含这个下标。左边界要包含这个下标,右边界又不要包含这个下标,是不是矛盾啦,它就不是个合法区间,所以不能写成 while(left <= right)
· 假如写成 while(left < right),那么 left == right,就不会进入循环,就不会出现上面说的“左边界要包含这个下标,右边界又不要包含这个下标”,所以它就是个合法区,写成 while(left < right)
3、左右区间如何缩小(何时加1,何时不加)
left = mid ? 还是 left = mid + 1 ?还是 right = mid ? 还是 right = mid - 1 ?
来看这个: if(target < nums[ mid ])接下来要干嘛?就缩小搜索搜索范围哈。带你分析一波:
因为 target < nums[ mid ],注意,nums[ mid ] 已经大于 target 了,就是说 nums[ mid ] 这个元素一定不是 target,mid 指向的这个元素一定不是要的结果。此时就又要回去最基础的那步了,【明确区间定义】,你定义的区间是什么?[left,right],[left,right),(left,right),还是.....?
假如是 [left,right]【此时是 while(left <= right)】,根据 if(target < nums[ mid ]),就要改变右边界,right = mid - 1,因为:mid 指向的元素绝对不是要的结果,区间定义有包含了右边界,所以 right = mid - 1。为什么不是 right = mid ?因为mid指向的元素绝对不是要的结果,区间定义又包含了右边界,把右边界又取到mid,本来下一次搜索区间不能再包含 mid 指向的元素的,又把它加进去,下一次搜索的范围就错了,这就是边界改变错的根源!会影响到最后的结果。
那如果区间定义是 [left,right)【此时是 while(left < right)】,if(target < nums[ mid ]),mid 指向的元素绝对不会是最后的结果,此时 right = mid。为什么不是 right = mid - 1?因为区间定义中,是不包含右边界的,把 right = mid,在下一次的搜索区间不会包含 mid 指向的元素,搜索区间就正确了嘛。
最后:
我不知道大家是否能够看懂,我已经尽力去解释了,如果第一遍没懂,就再多读几遍,它一定一定一定一定会对你有帮助!!!切记不要背这些代码,理解到了根本不需要背这个二分,随便写!
呈上举例的代码:(如果对 & 不知道怎么使用的,欢迎看我的另一篇文章,嘻嘻嘻)
int binary_Search(vector<int> &nums, int x)
{
// 区间定义成 [left, right]
int left = 0, right = nums.size() - 1;
while (left <= right) // 根据区间定义,所以 left <= right
{
int mid = (left + right) / 2;
if (x < nums[mid])
right = mid - 1; // 根据区间定义,包含右边界
else if (x > nums[mid])
left = mid + 1; // 根据区间定义,包含左边界
else
return mid;
}
return -1;
}
int binary_Search(vector<int> &nums, int x)
{
// 区间定义成 [left, right)
// 要对整个 nums[] 进行搜素,right 就是数组长度-1
// 因为区间定义成 [left, right),不包含右边界,所以要是 right = nums.size()
int left = 0, right = nums.size();
while (left < right) // 根据区间定义,所以 left < right
{
int mid = (left + right) / 2;
if (x < nums[mid])
right = mid; // 根据区间定义,不包含右边界
else if (x > nums[mid])
left = mid + 1; // 根据区间定义,包含左边界
else
return mid;
}
return -1;
}
最后的最后,写一篇文章真不容易呀,哈哈哈哈哈哈,如果对你有帮助,点个赞呗 ^_^,谢谢啦~~
如果有问题,放心大胆地指出来,我一定洗耳恭听~~~