二分查找算法整理

        二分查找算法,基本思想很简单:利用查找待集合中元素的单调性,来一步步逼近目标元素的位置。例如,在一个升序排列的A数组中,查找值为w的元素位置。用位于数组中部的元素A[mid]和w比较:若A[mid]=w,则找到目标元素的位置;若A[mid]<w,则说明w在数组的右半边,下次查找将在右半边进行;若A[mid]>w,则说明w在左半边,下次查找在左半边进行。

       虽然思想简单,可是想实现一个正确的二分查找算法还是有难度的,至少对我来说是这样。现在就来把二分查找算法整理一下,温故知新,也方便以后参考。

查找值的位置 

       这是最基本的二分查找,假定数组中没有重复元素,代码如下:

	static int binarySearch(int[] array,int key)
	{
		int begin=0;
		int end=array.length-1;
		while(begin<=end)
		{
			int mid=begin+(end-begin)/2;
			if(array[mid]==key)
				return mid;
			else if(array[mid]>key)
				end=mid-1;
			else
				 begin=mid+1;
		}
		return -1;
	}
代码容易理解,但是有几点要注意

(1). 判断条件为begin<=end,容易写成begin<end。若是begin<end,在查找时,会出现错误。 

            错误原因:若查找最后一个元素时,进行到begin=n-2,end=n-1时,mid=(begin+end)/2,由于整数除是取地板,mid=begin。进入循环,A[mid]小于目标值,begin=mid+1, 则begin=end。这时候会直接结束循环,得出的结果是目标值不在数组中。

(2). begin和end的赋值,容易错写成begin=mid,end=mid. 这样写的错误不容易发现。你会发现查找前面n-1个元素都能得出正确

结果。但是查找最后一个元素以及不存在的元素时,程序会陷入死循环。

            错误原因:查找最后一个元素以及不存在的元素时。到最后,begin=n-i,end=n-i+1,mid=begin(原因如上)。这时候,目标值大于mid, begin=mid, 回到之前的状态。begin一直小于end,因此会陷入死循环。


变体一 查找第一个目标值、最后一个目标值

        上面的情况假定每个值在数组中只有一个,如果现在数组中可能会有多个相同的值呢?下面的代码会返回这些值中第一个和最后一个的位置。

   
	static int binarySearch_firstX(int[] array,int x)
	{
		int begin=0,end=array.length-1,mid;
		while(begin<end)
		{
			mid=begin+(end-begin)/2;
			if(x<=array[mid])
			{
				end=mid;
			}else
			{
				begin=mid+1;
			}
		}
		if(array[end]==x) return end;
		return -1;
	}


	static int binarySearch_lastX(int[] array,int x)
	{
		int begin=0,end=array.length-1,mid;
		while(begin<end)
		{
			mid=begin+(end-begin+1)/2;
			if(array[mid]<=x)
			{
				begin=mid;
			}else
			{
				end=mid-1;
			}
		}
		if(array[begin]==x) return begin;
		return -1;
	}
            (1)binarySearch_firstX查找第一个x的位置,注意不是一找到等于x的值就立刻返回。查找x的第一个位置时,若x<=array[mid],则firstx该在mid的左边或者等于mid,因此end=mid;若x>array[mid],则firstx肯定在mid右边,得begin=mid+1。

         注意循环的结束条件是begin<end。若误写成begin<=end,查询时会进入死循环。 错误原因:

         1 . 若查询到最后出现begin=firstx,end=firstx+1,mid=(begin+end)/2----->mid=begin,则x=array[mid] ,end<---mid,end与begin相等,此后每次循环end=begin,死循环。

         2 . 若查询到最后出现begin=firstx-1,end=first,mid=(begin+end)/2------->mid=begin,则x>array[mid],begin<---mid+1,end与begin相等,与上面同理,是死循环。

      (2)binarySearch_lastX查找最后一个x的位置。查找最后一个x的位置时,若x>=array[mid],lastx在mid的右边或者等于mid,得到begin=mid;若x<array[mid],则lastx一定在mid的左边,得到end=mid-1。

        循环结束条件是begin<end,并且要注意,mid不在是(begin+end)/2,而是(begin+end+1)/2,即这次不再是取地板了。

        如果错写成前面一种情况,查询时会发生死循环。错误原因:

        1 . 若查询到最后,出现begin=lastx,end=lastx+1,按照错误方式计算mid,得到mid=begin。A[mid]=x,继续将begin赋为mid,begin会在后面的循环中一直小于end,死循环。而用正确方式计算mid,得到mid=begin+1,此时A[mid]>x, end<---mid-1,得到end和begin相等,顺利跳出循环,并且end和begin都指向lastx。

        2 . 查询到最后,出现begin=lastx-1,end=lastx。按照错误方式计算mid,得到mid=begin,A[mid]=x,继续将begin赋为mid,begin会在后面的循环中一直小于end,死循环。而用正确方式计算mid,mid=begin+1,A[mid]=x,begin<---mid,得到begin=end,顺利跳出循环,并且end和begin都指向lastx。

             待续未完……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值