二分查找的种种

1.不存在重复元素

(1)找到目标值

private int binarySearch(int[] nums,int target) {
    int left=0,right=nums.length-1;
    while(right>=left)
    {
        int mid=left+(right-left)>>1;
        if(nums[mid]==target)
         return mid;
        if(nums[mid]<target)
	     left=mid+1;
        else if(nums[mid]>target)
	     right=mid-1;
     }
   return -1;
}

(2)寻找比目标值大的元素的下标

private int binarySearch(int[] nums,int target) {
    int left=0,right=nums.length-1;
    while(right>=left)
    {
        int mid=left+((right-left)>>1);
        if(nums[mid]>target)
	      right=mid-1;
        else if(nums[mid]<=target)
	      left=mid+1;
    }
    if(left>nums.length-1)
	return -1;
    return left;
}

(3)比目标值小的元素的下标

private int binarySearch(int[] nums,int target) {
	int left=0,right=nums.length-1;
    while(right>=left)
    {
       int mid=left+((right-left)>>1);
       if(nums[mid]>=target)
	     right=mid-1;
       else if(nums[mid]<target)
	     left=mid+1;
    }
 
  return right;
}

2.存在重复元素

(1)找到第一个等于目标值的元素

private int binarySearch(int nums[],int target){
 int left=0;
 int right=nums.length-1;
 while(right>=left){
   int mid=left+(right-left)>>1;
   if(nums[mid]==target){
      if(mid==0||nums[mid-1]!=target)
        return mid;
      else//说明前面有重复元素
        right=mid-1;
   }else if(target>nums[mid]){
      left=mid+1;
    }else if(target<nums[mid])
     right=mid-1;
    
  }
  return -1;
}

(2)最后一个等于目标值的

private int binarySearch(int nums[],int target){
 int left=0;
 int right=nums.length-1;
 while(right<=left){
  int mid=left+(right-left)>>1;
  if(target==nums[mid]){
    if(mid==length-1||nums[mid+1]!=target)
        return mid;
    else
        return left=mid+1;
   }else if(target>nums[mid]){
     return left=mid+1;
   }else if(target<nums[mid]){
    return right=mid-1;
   }

 }
 return -1;
}

(3)找到第一个大于等于目标值的

private int binarySearch(int nums[],int target){
 int left=0;
 int right=nums.length-1;
 while(left<=right){
  int mid=left+(right-left)>>1;
  if(target<=nums[mid]){
        if(mid==0||nums[mid-1]<target)
           return mid;
        else
           right=mid-1;
  }else if(target>nums[mid]){
      left=mid+1;
  }
 }
 return -1;
}
  

(4)找到最后一个小于等于目标值的元素

private int binarySearch(int nums[],int target){
 int right=nums.length;
 int left=0;
 while(right>=left){
   int mid=left+(right-left)>>1;
   if(nums[mid]<=target){
      if(mid==nums.length||nums[mid+1]>target)
            return mid;
      else
            left=mid+1;
    }else
     right=mid-1;
  }
 reurn -1;
}

 

改进二分法:

方法一:插值寻找算法

每次二分选的都是中间数作为关键字划分区间的,如果1-1000,查10,从500划分,效率就会低。插值查找就是改变划分的比例,我觉得就是找到target与最大值,最小值之间一个比例。
改进主要体现在mid上
原来:mid=left+1/2(right-left)
现在:mid=left+(target-nums[left])/(nums[right]-target)*(right-left)
时间复杂度O(logn)

方法二:斐波那契查找算法

用黄金分割比来实现划分。
斐波那契:f[n]=f[n-1]+f[n-2];
改成:      mid=left+f[k-1]-1;

它要求开始表中记录的个数比某个斐波那契数小1,即n=f[k]-1;
为什么n=f[k]-1?
因为如果表中数据为f[k]-1个,mid分割用掉一个,剩下f[k]-2个,正好分给两个子序列:(f[k-1]-1)+(f[k-2]-1)
将target和mid值比较:
1.target==nums[mid]
返回mid对应的元素
2.target>nums[mid]
left=mid+1,k-=2.
【left=mid+1,说明target在[mid+1,right],k-=2说明[mid+1,right]这个范围内的元素个数为f[k-2]-1】
3.target<nums[mid]
right=mid-1,k-=1.
【right=mid-1,说明target在[left,mid-1],k-=1说明[left,mid-1]这个区间的元素个数为f[k-1]-1】
时间复杂度O(logn)
 

int maxSize=10;
	private int binarySearch(int[] nums,int target) {
	  int left=0,right=nums.length-1;
	  int mid=0,k=0;
	  int f[]=fib();
	  while(right>f[k]-1)
		  k++;
	  //拷贝k个nums数组到tmp
	  int tmp[]=Arrays.copyOf(nums, k);
	  //对多余的斐波那契数组进行填充
	  for(int i=right+1;i<tmp.length;i++)
		  tmp[i]=nums[right];
	  while(left<=right)
	  {
		  mid=left+f[k-1]-1;
		  if(target>nums[mid])
		  {
			  left=mid+1;
			  k-=2;
		  }
		  else if(target<nums[mid])
		  {
			  right=mid-1;
			  k-=1;
		  }
		  else{
			  if(mid<=right)
				  return mid;
			  else
				  return right;
		  }
	  }
	  return -1;
	}
	private int[] fib() {
		int f[]=new int[maxSize];
		f[0]=1;
		f[1]=1;
		for(int i=2;i<maxSize;i++)
			f[i]=f[i-1]+f[i-2];
		return f;
	}
	public static void main(String[] args) {
		int []nums={-1,0,3,5,5,5,9,12};
		Test t=new Test();
		int ans=t.binarySearch(nums,5);
		System.out.println(ans);
	}

性能比较:
斐波那契查找算法>折半查找算法>插值查找算法
因为
mid=left+f[k-1]-1;
mid=(left+right)/2
mid=left+(target-nums[left])/(nums[right]-nums[left])*(right-left)
斐波那契查找算法只用了+,-运算
折半查找算法用了/,+运算
插值查找算法用了+-*/四则运算,因此有如上结论。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值