[置顶] 二分查找各种情况大总结
来源:互联网 发布:java反序列化代码 编辑:程序博客网 时间:2024/06/05 09:22
二分查找多次刷题时遇到,虽然每次也能写对,但花了蛮多时间,没好好想过。而且网上的太多版本,并不是很简洁,而且边界条件变化情况太多,容易混淆,下面是自己对二分查找的一些思考和总结,尽量写得简单易懂。
三种基本版本:
1.1 二分查找原始版--查找某个数的下标(任意一个)
在有序数组中查找某个数,找到返回数的下标,存在多个返回任意一个即可,没有返回-1。所有程序采用左右均为闭区间,即函数中n为最后一个元素下标,而不是元素个数。典型代码如下:
- public int binarySearch(int[] a, int n, int key){
- //n + 1 个数
- int low = 0;
- int high = n;
- int mid = 0;
- while(low <= high) {
- mid = low + ((high-low) >> 1);
- if(key == a[mid]) {
- return mid;
- } else if(key < a[mid]) {
- high = mid - 1;
- } else {
- low = mid + 1;
- }
- }
- return -1;
- }
1.2 查找第一个大于等于某个数的下标
例:int[] a = {1,2,2,2,4,8,10},查找2,返回第一个2的下标1;查找3,返回4的下标4;查找4,返回4的下标4。如果没有大于等于key的元素,返回-1。
下面是代码,改动只有两处:
- public int firstGreatOrEqual(int[] a, int n, int key){
- //n + 1 个数
- int low = 0;
- int high = n;
- int mid = 0;
- while(low <= high) {
- mid = low + ((high-low) >> 1);
- if(key <= a[mid]) {
- high = mid - 1;
- } else {
- low = mid + 1;
- }
- }
- return low <= n ? low : -1;
- }
1、条件为key<=a[mid],意思是key小于等于中间值,则往左半区域查找。如在 {1,2,2,2,4,8,10}查找2,第一步,low=0, high=6, 得mid=3, key <= a[3],往下标{1,2,2}中继续查找。
2、终止前一步为: low=high,得mid = low,此时如果key <= a[mid],则high会改变,而low指向当前元素,即为满足要求的元素。如果key > a[mid],则low会改变,而low指向mid下一个元素。
3、如果key大于数组最后一个元素,low最后变为n+1,即没有元素大于key,需要返回 -1。
1.3 查找第一个大于某个数的下标
例:int[] a = {1,2,2,2,4,8,10},查找2,返回4的下标4;查找3,返回4的下标4;查找4,返回8的下标5。如果没有大于key的元素,返回-1。
如下是代码,与上面大于等于某个数仅判断一个符号不同:
- public int firstGreat(int[] a, int n, int key){
- //n + 1 个数
- int low = 0;
- int high = n;
- int mid = 0;
- while(low <= high) {
- mid = low + ((high-low) >> 1);
- if(key < a[mid]) {
- high = mid - 1;
- } else {
- low = mid + 1;
- }
- }
- return low <= n ? low : -1;
- }
以上原型的扩展应用
2.1 查找数组中某个数的位置的最小下标,没有返回-1
直接用上面1.2,但需要处理一下,即当返回的low位置不等于key时,也返回-1。如下:
- public int firstIndex(int[] a, int n, int key){
- //n + 1 个数
- int low = 0;
- int high = n;
- int mid = 0;
- while(low <= high) {
- mid = low + ((high-low) >> 1);
- if(key <= a[mid]) {
- high = mid - 1;
- } else {
- low = mid + 1;
- }
- }
- return (low <= n) && (a[low] == key) ? low : -1;
- }
2.2 查找数组中某个数的位置的最大下标,没有返回-1
直接用上面1.3,得到的下标 - 1即可,但也需要处理一下,即不用判断low <= n,而是判断low-1>=0,且返回的low-1位置等于key时,才返回位置low - 1。如下:- public int lastIndex(int[] a, int n, int key){
- //n + 1 个数
- int low = 0;
- int high = n;
- int mid = 0;
- while(low <= high) {
- mid = low + ((high-low) >> 1);
- if(key < a[mid]) {
- high = mid - 1;
- } else {
- low = mid + 1;
- }
- }
- return (low - 1 >= 0 && (a[low - 1] == key))? low - 1: -1;
- }
2.3 查找数组中小于某个数的最大下标,没有返回-1
直接用上面1.2,返回的low-1位置即为可能的位置。也需要处理一下,需要low-1>=0。- public int firstLess(int[] a, int n, int key) {
- // n + 1 个数
- int low = 0;
- int high = n;
- int mid = 0;
- while (low <= high) {
- mid = low + ((high - low) >> 1);
- if (key <= a[mid]) {
- high = mid - 1;
- } else {
- low = mid + 1;
- }
- }
- return (low - 1 >= 0) ? low - 1 : -1;
- }
2.4 查找数组中某个数的出现次数
直接使用1.2,1.3即可,但需要修改1.2,1.3的返回条件,当low>n时,直接返回low。举例说明:int[] a = {1,2,2,2,4,8,10},查找10的个数,根据1.2程序得到low = 6,而1.3程序返回值 = -1,不符合。因此需要将1.3得到的low = n + 1值返回即可。同理,如果查找15,此时1.2程序返回值为-1,也得改为直接返回low = n+1。使用1.2,1.3,分别求得下界first和上界last,两个相减即(last - first)是key出现次数。如下:
- public int getCount(int[] a, int n, int key) {
- // n + 1 个数
- int first = firstGreatOrEqual2(a, n, key);
- int last = firstGreat2(a, n, key);
- return last - first;
- }
- public int firstGreatOrEqual2(int[] a, int n, int key) {
- // n + 1 个数
- int low = 0;
- int high = n;
- int mid = 0;
- while (low <= high) {
- mid = low + ((high - low) >> 1);
- if (key <= a[mid]) {
- high = mid - 1;
- } else {
- low = mid + 1;
- }
- }
- return low;
- }
- public int firstGreat2(int[] a, int n, int key) {
- // n + 1 个数
- int low = 0;
- int high = n;
- int mid = 0;
- while (low <= high) {
- mid = low + ((high - low) >> 1);
- if (key < a[mid]) {
- high = mid - 1;
- } else {
- low = mid + 1;
- }
- }
- return low;
- }
总结下:
1、写此文章的目的是总结多种二分查找相似问题,网上的太多代码,边界条件杂乱且不容易理解。这里先总结出最基本的三种情况代码,再用三种情况的代码求解相似问题,理解复杂度降低。
2、一些问题,需要自己多总结,才能有理解深刻,才能写出最适合自己理解的代码。
本文代码已测试过,如有错误,请不吝赐教。
- [置顶] 二分查找各种情况大总结
- 二分查找各种情况大总结
- 二分查找各种情况大总结
- 二分查找各种情况大总结
- 二分查找各种情况汇总
- 各种二分查找算法总结
- 二分查找的各种情况实现以及一些注意点
- 各种二分查找
- 各种二分查找
- opencv各种内存泄露情况的大总结
- opencv各种内存泄露情况的大总结
- opencv各种内存泄露情况的大总结
- opencv各种内存泄露情况的大总结
- opencv各种内存泄露情况的大总结
- 三大查找算法总结:二分查找,分块查找和哈希表查找。
- 二分查找的各种条件
- 二分查找及各种变化
- python之各种二分查找
- 读书笔记:Spark上数据的获取,处理与准备 上
- Ubuntu初次使用
- Subsequence
- mongodb 查询数据
- /home/zp/my-imx6/03_tools/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin/arm-linux-gnueabihf-g
- [置顶] 二分查找各种情况大总结
- P1017 进制转换
- --------------------------------开始动手自己写一个简易的操作系统-----------------------------------------0
- 基于@Aspect的AOP配置
- LeetCode653. Two Sum IV
- 线段树 (更新区间查询区间)poj 3468 A Simple Problem with Integers
- 【JDBC笔记】JDBC基础知识
- SpringData-jpa maven项目 cannot resolve symbol 'JdbcTemplate' 飘红
- Maven管理(依赖范围,依赖传递,依赖冲突,依赖可选,依赖排除)