二分搜索,呵呵
2016-07-25 17:27
387 查看
二分搜索 呵呵
写在前面的话:
二分搜索原理看似都懂,但是真的让我敲起来,我还真不敢保证写的一定没有bug,甚至陷入bug里不知道怎么改呢.其实,二分法真的不是那么简单,尤其是二分的各个变种.下面一次介绍各种变种二分法,并且加上我的总结吧.
1. 基本二分法
在一个排好序的数组里查找一个key值.int search(int *arr, int n, int key) { int left = 0, right = n-1; while(left<=right) { int mid = left + ((right - left) << 1); if (arr[mid] == key) return mid; else if(arr[mid] > key) right = mid - 1; else left = mid + 1; } return -1; }
如果条件变化成, 数组中的数据可能重复,要求范围等于key值最小的下标,或者找出数组中第一个大于(或等于)key值的元素下标,请看下面
2. 找出第一个与key相等的元素
int searchFirstEqual(int *arr, int n, int key) { int left = 0, right = n-1; while(left<=right) { int mid = (left+right)/2; if(arr[mid] >= key) right = mid - 1; else if(arr[mid] < key) left = mid + 1; } if( left < n && arr[left] == key) return left; return -1; }
3. 找出最后一个与key相等的元素
int searchLastEqual(int *arr, int n, int key) { int left = 0, right = n-1; while(left<=right) { int mid = (left+right)/2; if(arr[mid] > key) right = mid - 1; else if(arr[mid] <= key) left = mid + 1; } if( right>=0 && arr[right] == key) return right; return -1; }
4. 找出第一个大于或等于key的元素
int searchFirstEqualOrLarger(int *arr, int n, int key) { int left=0, right=n-1; while(left<=right) { int mid = (left+right)/2; if(arr[mid] >= key) right = mid-1; else if (arr[mid] < key) left = mid+1; } return left; }
5. 找出第一个大于key的元素
int searchFirstLarger(int *arr, int n, int key) { int left=0, right=n-1; while(left<=right) { int mid = (left+right)/2; if(arr[mid] > key) right = mid-1; else if (arr[mid] <= key) left = mid+1; } return left; }
6. 找出最后一个小于或等于key的元素
int searchLastEqualOrSmaller(int *arr, int n, int key) { int left=0, right=n-1; while(left<=right) { int m = (left+right)/2; if(arr[m] > key) right = m-1; else if (arr[m] <= key) left = m+1; } return right; }
7. 找出最后一个小于key的元素
int searchLastSmaller(int *arr, int n, int key) { int left=0, right=n-1; while(left<=right) { int mid = (left+right)/2; if(arr[mid] >= key) right = mid-1; else if (arr[mid] < key) left = mid+1; } return right; }
我的总结:
各种二分有一些代码部分是不变的,记住就好.也很容易记住.int search(int *arr, int n, int key) { int left=0, right=n-1; while(left<=right) { int mid = (left+right)/2; if(arr[mid] /****/ key) right = mid-1; else if (arr[mid] /****/ key) left = mid+1; } return /****/; }
其中除了3处/****/之外,其他部分可以说是模板,很简短,用的时候敲出来就行了
返回值问题:
记住left和right是key值存在的范围. 所以当要找最后一个等于key时,必然返回right . 当要找最大的小鱼key值时, 必然返回left
a[mid] == key 时, 到底是改变left还是right?
这里要用 "不会更坏的"想法来理解比较好.
比如,当我们知道要 返回值 和 条件是否包括等于 后,我们再来看等号如何处理.
比如:条件包括等号,返回值为left . 那么就意味着不取等号那种情况后所做的操作不可以将left变化成mid+1,因为一旦这样变化,最终返回left的时候不再可能是mid,而mid是符合条件(包括等于)要求的 . 这肯定是不合理的.所以等号绝不是加在这里 .
说到这里,我们的返回值和等号加在哪里,2个问题都解决了,就可以写完美的二分法啦!
![](http://oav5qmduk.bkt.clouddn.com/gaoqing.jpg)
相关文章推荐
- jQuery仿京东商城楼梯式导航定位菜单
- 有序数组中插入位置
- 在大数据圈你不知道的15个新技术
- solr 的suggest详细使用
- 隐马尔科夫模型(HMMs)之一:简介及生成模式
- 使用Redis存储Nginx+Tomcat负载均衡集群的Session
- CodeForces 455B A Lot of Games
- linux eclipse3.6.1 maven安装
- std::bind源码剖析
- 代码中动态该变shape的颜色
- HashMap使用Comparator排序
- chmod 改变文件所有者
- 关于java中map存储多行的小程序(一键对多值)
- DES加解密算法
- 跟我学Android UI高级之十五碎片Fragment
- 不要让忙碌成为逃避学习的借口--Blog开通,博主介绍
- jQuery查看dom元素上绑定的事件列表
- 跟我学Android UI高级之十五碎片Fragment
- 跟我学Android UI高级之十五碎片Fragment
- Yii2 behavior运用