关于二分算法的一些思考

来源:互联网 发布:linux glibc 升级 编辑:程序博客网 时间:2024/06/05 05:13

一、 二分算法简述:  

二分算法能够实现有序表的快速查找运算,复杂度为O(logn),关于二分运算的实现问题和解决办法,做了几个简单的思考。

二、 二分要解决的问题:

①   问题一:元素各异,排列有序,查找元素位于表内的二分查找

②   问题二:元素各异,排列有序,查找元素不在表内的二分查找(分两种情况:返回大于此数的最小值或返回小于此数的最大值)

③   问题三:元素重复,排列有序,查找元素位于表内的二分查找(分两种情况:返回最左端元素下标和返回最右端元素下标)

④   问题四:元素重复,排列有序,查找元素不在表内的二分查找(分两种情况:返回小于此数的最大值或者大于此数的最小值)

三、 二分的解决方案:

1.    概念的几个定义:

①    左指针右穿:左值针大于了右指针,成为左值针右穿

②    右指针左穿:右指针小于了左值针,称为右指针左穿

这个是while循环终止也就是查找正确的条件。

2.    具体解决方案:

①     问题一:

while(ll<= rr) {

        int mid = (ll+rr)>>1;

        if(k<=se[mid]) rr = mid – 1;

        else ll = mid + 1;

}

Returnll;

代码性质:

a)    位置一(ll<=rr):这样保证while循环是由左指针右穿或者右指针左穿而终止的。

b)    位置二(k<=se[mid])将k = se[mid]的时候仍然更新右指针是为了保证有重复结点的时候能够统一处理为“查找比k小一点点儿的元素的”的处理方法。

c)     位置三、四(rr = mid – 1, ll = mid+ 1)保证每一次循环都有更新,而且保证终止条件必须是左穿或者右穿

d)    位置五(return ll):确保左值针右穿的时候是找到了比“查找比k小一点点儿的元素”稍微大一点儿的元素;或者右指针左穿的时候也是找到了比“查找比k小一点点儿的元素”稍微大一点儿的元素;

e)    总结:通过以上位置一至五确定了查找的元素是大于等于k的第一个元素

对于此题而言,直接使用返回值ll即可。

②     问题二:

两种情况分别返回ll-1和ll。

③     问题三:

第一种情况返回ll,第二种情况需要进一步讨论:

While(ll<rr){

        Int mid = (ll+rr)>>1;

        If(k<se[mid]) rr = mid – 1;

        Else ll = mid + 1;

}

Returnrr;//或者返回ll-1

因为这个时候假设“查找某一个比k稍微大一点儿的元素”,所以查找成功的是比k大的最小的元素,因为要返回的是最右边的值等于k的元素下标,所以说直接返回ll-1,即rr可以了。

④     问题四:

分别返回ll-1和ll即可。

四、 有关边界的情况

因为跳出循环的条件是ll<=rr,所以说不存在数组越界的情况。

五、 其他写法的几个常见问题:

错误一:使用rr = mid和ll = mid可能出现的死循环,因为mid是偏左性质的,所以说可能因为se[mid] < k而更新的是ll,但是与此同时ll==mid,相当于没有更新,而陷入死循环,例如se = {4,5},需要查找值为5的元素,这个时候就会陷入死循环。

六、 Set集合写法so easy:

low_bound()函数,返回的是大于等于此元素的第一个元素,相当于ll

upper_bound()函数,返回的是大于此元素的第一个元素,相当于更改等于位置后的ll。

思维分析关键点总结:在于对“查找比k小一点儿点儿的元素”的假设查找方式。       

六.图像分析

可以简单记忆为:

1 1 1 2 2 2 2 33

low_bound就是ll指向第一个2

upper_bound就是ll-1指向第一个3