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)
斐波那契查找算法只用了+,-运算
折半查找算法用了/,+运算
插值查找算法用了+-*/四则运算,因此有如上结论。