循环递增数组的二分查找

来看看标准的二分查找,复杂度O(lgn)

int BinarySearch(const int *a,int low,int high,int key){
	int mid;
	while(low <= high){ //注意这里是 <=,在low跑到跟high回合的时候还在进行,直到low>high
		mid = low + (high-low)>>1; //防止( low + hign ) 会发生溢出
		if(a[mid] == key){
			return mid;
		}else if(a[mid]<key){
			low = mid + 1;
		}else
			hign = mid - 1;
	}
	return low;
}


现在来看看,什么是循环递增数组,如A[]={ 17 19 20 25 1 4 7 9} 就是个循环递增数组,那在这样的一个区间中寻找某个key,怎样操作,看看标准的二分是怎样做的,先知道mid所在的元素,然后让key跟mid所在元素比较,因为总能保持low所在元素比mid小,high所在元素比mid大,但是在循环递增数组中是不会保持这样的性质的(在某些情况下是会保持),这样寻找的key的位置就有这样几种可能:

(1)key将前后的数组分成两部分,key刚好是第一部分的最后元素,这样整个数组还是循环递增的

(2)key将落在原来数组的第二部分,这样整个数组以key为准,又分割成了一个循环递增数组,前面循环递增后面严格递增

(3)key将落在原来数组的第一部分,这样以key为准,分割成了循环递增数组,前面严格递增后面循环递增

比如上面的数组中,如果查找20,那么前17 19 是个严格的递增数组和后面 25 1 4 7 9 就又是个循环递增数组

如果查找19,那么前17 和后面的 20 25 1 4 7 9也满足上述性质

如果查找4,那么前面的是个循环递增数组和后面的严格递增数组

所以循环递增数组就有个这样的性质:key将数组分割成两部分,一部分要么是循环递增,另一部分要么是严格递增,或者相反(针对升序数组)

所以在写的时候就要在原来二分的基础上加点条件:

设要查找的元素是key,数组的首元素是a[low],中间元素是a[mid],最后一个元素是a[high],当a[mid]!=key时:

(1)当a[low]<a[mid]说明前半部分是严格递增的,那么后半部分就是循环递增的,如果a[key]<a[mid]&&a[key]>=a[low],那就key就落在前半部分,否则就在后半部分的循环递增数组中。

(2) 当a[mid]<a[high] 那么前半部分就是循环递增,后半部分就是严格递增的,那么a[key]>a[mid] && a[key]<=a[high],那key就落在后半部分,否在就在前半部分的循环递增数组中。根据这样的思路写出代码:

// 改进后的二分搜索
int Search(int A[],int n, int num)
{
	int left = 0 ;
	int right = n - 1 ;
	int mid = 0 ;
	int pos = -1 ;  

	while(left <= right)
	{
		mid = (left + right)/2 ;

		if (A[mid] == num)
		{   
			pos = mid ; 
			break;     
		}
		if (A[left] <= A[mid])    //前半部分是严格递增的,后半部分是一个更小的循环递增数组
		{
			if(A[left] <= num && num  < A[mid] )
			{
				right = mid - 1 ;
			}
			else
			{
				left = mid + 1 ;
			} 
		}
		else    //后半部分是严格递增的,前半部分是一个更小的循环递增数组
		{
			if ( A[mid] < num && num <= A[right])
			{
				left = mid + 1 ;
			}
			else
			{
				right = mid - 1 ;
			}
		}
	}
	return pos;
}
所以循环递增数组的核心还是在怎样寻找合适的low和high,而不是平常的二分的low=mid+1或者hign = mid-1.

以下给出一个完整的在循环递增数组中查找元素的例程:

<pre name="code" class="cpp" style="color: rgb(51, 51, 51); font-size: 14.399999618530273px; line-height: 26px;">#include<iostream>
using namespace std;int findPos(int *arr,int key,int low,int high){int pos = -1;// if can't find the element,return -1 reprement no find

 
int mid;
  while(low <= high){
        mid = (low+high)/2;
        if(arr[mid]==key){
                pos = mid;
                break;
        }
  if(arr[low]<=arr[mid]){ // the  first part is gradually increase
        if(arr[mid]>key && arr[low]<=key ) high = mid-1;
        else low = mid+1;
}else{
        if(arr[mid]<key && arr[high]>=key) low = mid +1;
        else high = mid -1;
}
}
return pos;
}
int main(){

int arr[]={17,19,20,25,1,4,7,9};
cout<<findPos(arr,19,0,7)<<endl;
return 0;
}
-- INSERT --




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值