二分查找各种情况大总结

原创 2016年08月30日 21:40:16

二分查找多次刷题时遇到,虽然每次也能写对,但花了蛮多时间,没好好想过。而且网上的太多版本,并不是很简洁,而且边界条件变化情况太多,容易混淆,下面是自己对二分查找的一些思考和总结,尽量写得简单易懂。


三种基本版本:

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、一些问题,需要自己多总结,才能有理解深刻,才能写出最适合自己理解的代码。

本文代码已测试过,如有错误,请不吝赐教。




版权声明:本文为博主原创文章,未经博主允许不得转载。

二分查找算法(递归与非递归两种方式)

首先说说二分查找法。 二分查找法是对一组有序的数字中进行查找,传递相应的数据,进行比较查找到与原数据相同的数据,查找到了返回1,失败返回对应的数组下标。 采用非递归方式完成二分查找法。java代码如...
  • lovesummerforever
  • lovesummerforever
  • 2014年04月27日 15:44
  • 86914

浅谈-二分查找

一、什么是二分查找? 1、我们首先引入这样一个问题:如果规定某一科目成绩分数范围:[0,100],现在小明知道自己的成绩,他让你猜他的成绩,如果猜的高了或者低了都会告诉你,用最少的次数猜出他的成绩,你...
  • mengxiang000000
  • mengxiang000000
  • 2016年10月07日 20:54
  • 3243

二分查找

转自:http://www.cppblog.com/converse/archive/2009/10/05/97905.html 二分查找算法基本思想 二分查找算法的前置条件是,一个已经排序好的序...
  • sunmenggmail
  • sunmenggmail
  • 2012年05月07日 08:34
  • 38024

二分查找(递归与非递归)

递归方法 int BinSearch(int Array[],int low,int high,int key/*要找的值*/){ if (low
  • q3498233
  • q3498233
  • 2009年08月06日 17:04
  • 44398

【C/C++】折半查找(二分查找)

一、二分查找 在C和C++里,二分查找是针对有序数组所用的一种快速查找元素的方法。 二、二分查找的条件以及优缺点 条件:针对有序数组(元素从小到大或从大到小) 优点:查询速度较快,时间复杂度为O(n)...
  • qq_31828515
  • qq_31828515
  • 2016年06月30日 17:34
  • 14571

二分法与牛顿法

二分法与牛顿法
  • hellotomhaha
  • hellotomhaha
  • 2017年02月12日 18:51
  • 591

二分查找(Binary Search)常见问题解决方法总结

缘由 今天浏览 何登成的技术博客  无意中发现了写的blog,二分查找(Binary Search)需要注意的问题,以及在数据库内核中的实现。 随想总结下二分查找的常见问题。 ...
  • han____shuai
  • han____shuai
  • 2017年07月17日 16:07
  • 373

深入理解二分查找

来源:TC 译者:农夫三拳 二分查找是计算机科学中的一个基础算法。为了能够探究它,我们首先建立起理论支柱,然后使用它来正确的实现算法并且避免人人谈到的差1的错误。 Fin...
  • caobo1212
  • caobo1212
  • 2012年08月25日 01:58
  • 2146

二分查找法的实例分析

二分查找:折半查找,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如                     果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,...
  • clown05
  • clown05
  • 2016年05月30日 00:56
  • 1968

C语言二分查找法(指针和数组实现)

/* * 编写一个函数,对一个已排序的整数表执行二分查找。 * 函数的输入包括各异指向表头的指针,表中的元素个数,以及待查找的数值。 * 函数的输出时一个指向满足查找要求的元素的指针,当未查找到...
  • toto1297488504
  • toto1297488504
  • 2014年07月25日 23:20
  • 1237
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:二分查找各种情况大总结
举报原因:
原因补充:

(最多只允许输入30个字)