您的当前位置:首页正文

C++实现二分法详解

2021-04-10 来源:易榕旅网
C++实现⼆分法详解

⼆分法是在⼀个排好序的序列(数组,链表等)中,不断收缩区间来进⾏⽬标值查找的⼀种算法,下⾯我们就来探究⼆分法使⽤的⼀些细节,以及常⽤的场景:1. 寻找⼀个数;2. 寻找左侧边界;3. 寻找右侧边界。

⼀、⼆分法的通⽤框架

int binarySearch(vector& nums, int target){ int left=0, right=nums.size(); while(left < right) {

int mid=(left+right)/2; if(nums[mid] == target){

// 条件⼀:中间的值与⽬标值相同 }

else if(nums[mid] > target){

// 条件⼆:中间的值⼤于⽬标值 }

else if(nums[mid] < target){

// 条件三:中间的值⼩于⽬标值 } }

return -1; }

⾸先,我们先来分析⼀下右边界 right 的初始值:

1. 当 right=nums.size() 时,初始化的区间就变成了 [0,right−1][0,right−1],即 [0,right)[0,right);2. 当right=nums.size()-1 时,初始化的区间就变成了 [0,right][0,right]。

在第⼀种情况下,当 nums[mid] > target 时,需要将区间向左收缩,即 right=mid。这个做法的逻辑是:既然 mid 位置处⼤于 target,⽽查找区间⼜是 “左闭右开”,因此当 right=mid 时,新的查找区间变成了 [0,mid)[0,mid),这样才不会漏掉值。同理,当 nums[mid] < target 时,需要将区间向右收缩,即 left = mid+1,因为在 \"左闭右开\" 的区间下,新的查找区间变成 [mid+1,right)[mid+1,right) 才不会漏掉值。当⽬标值不在序列中时,需要将 while 的条件写成 while(left < right) ⽽不是写成 while(left<=right),这样会引起数组越界。第⼆种情况的分析类似,这⾥只给出结论:

当 nums[mid] > target 时,需要将区间向左收缩,即 right=mid-1;当 nums[mid] < target 时,需要将区间向右收缩,即 left = mid+1;当⽬标值不在序列中时,需要将 while 的条件写成 while(left<=right)

⼆、⼆分法查找⽬标值

在序列中查找⼀个数,如果存在则返回数的索引,如果不存在则返回 -1 。为了⽅便分析,我们就只⽤第⼀种情况进⾏说明:

int binarySearch(vector& nums, int target){ int left=0, right=nums.size(); while(left < right) {

int mid=(left+right)/2; if(nums[mid] == target){

return mid; // 查询到⽬标值,直接返回⽬标值的位置 }

else if(nums[mid] > target){

right = mid; // 中间的值⼤于⽬标值,向左收缩区间 }

else if(nums[mid] < target){

left = mid+1;// 中间的值⼩于⽬标值,向右收缩区间 } }

return -1; // 当没有找到,直接返回-1}

三、⼆分法查找⽬标值的左右边界

上述代码只能从序列中查找⼀个⽬标值并返回位置,当⼀个序列中⽬标值不⽌⼀个时,我们需要找到⽬标值最左边的位置和最右边的位置,这时候⼆分法需要进⾏改写:

// 查找⽬标值的左边界

int binarySearch(vector& nums, int target){ int left=0, right=nums.size(); while(left < right)

{

int mid=(left+right)/2; if(nums[mid] == target){

right = mid; // 查询到⽬标值不进⾏返回,⽽是收缩区间继续查找 }

else if(nums[mid] > target){

right = mid; // 中间的值⼤于⽬标值,向左收缩区间 }

else if(nums[mid] < target){

left = mid+1;// 中间的值⼩于⽬标值,向右收缩区间 } }

return left; }

根据上述代码,可以发现如果查找⽬标值的左边界,在满⾜ nums[mid] == target 时,需要缩⼩搜索区间的上界 right,在区间 [left,mid][left,mid] 中继续搜索,直到搜索完毕 left==right。此时 left=right=左边界。查找右边界的做法与左边界类似:

// 查找⽬标值的左边界

int binarySearch(vector& nums, int target){ int left=0, right=nums.size(); while(left < right) {

int mid=(left+right)/2; if(nums[mid] == target){

left = mid+1; // 查询到⽬标值不进⾏返回,⽽是收缩区间继续查找 }

else if(nums[mid] > target){

right = mid; // 中间的值⼤于⽬标值,向左收缩区间 }

else if(nums[mid] < target){

left = mid+1;// 中间的值⼩于⽬标值,向右收缩区间 } }

return left-1; }

注意这⾥的判断条件改成了当 nums[mid] == target 时,left = mid+1。因为搜索的区间为 \"左闭右开\",所以在寻找左边界时可令 right=mid ,在寻找右边界时必须另 left=mid+1,不然程序会⼀直停在循环⾥⾯⽽⽆法跳出循环。

因篇幅问题不能全部显示,请点此查看更多更全内容