二分查找,第一个等于给定值的元素下标

 

有些繁琐的递归实现

#include <stdio.h>
#include <limits.h>


//binary search, find first value == v element's index
//lt < , le <= , eq =, ne != , ge >=, gt >
int bin_s_first_eq(int* a, int l, int r, int v,int maxLtIndex, int minEqIndex){
    if(r<l)
        return -1;
    int m = l+((r-l)>>1);
    printf("a[m] a[%d]=%d ,maxLtI:%d,minEqI:%d
",m,a[m],maxLtIndex, minEqIndex);
    if(a[m] < v){
        maxLtIndex = maxLtIndex < m ? m : maxLtIndex;
        if(minEqIndex - maxLtIndex == 1){
            printf("find min:%d max:%d
",minEqIndex, maxLtIndex);
            return minEqIndex;
        }else{ //go right
            printf("go right
");
            return bin_s_first_eq(a,m+1,r,v,maxLtIndex, minEqIndex);
        }
    }else if(a[m] > v){
        //go left
        printf("go left
");
        return bin_s_first_eq(a,l,m-1,v,maxLtIndex, minEqIndex);
    }else{//eq
        minEqIndex = minEqIndex > m ? m : minEqIndex;
        if(minEqIndex - maxLtIndex == 1 || minEqIndex == 0){
            printf("find min:%d max:%d
",minEqIndex, maxLtIndex);
            return minEqIndex;
        }else{ //go left
            printf("go left
");
            return bin_s_first_eq(a,l,m-1,v,maxLtIndex, minEqIndex);
        }
    }
}

int bin_s_first_eq2(int* a,int n,int v){
    int l = 0;
    int h = n-1;
    int m;
    while(l<=h){
        m = l+((h-l)>>1);
        if(a[m] >= v)
            h = m-1;
        else
            l = m+1;
    }
    if(l<n && a[l]==v)
        return l;
    return -1;
}

void main(){
    int v = 9;
    //int a[] ={1,2,3,3,3,4,5,6,7,8,9};
    //int a[] ={1,2,3,4,5,6,6,6,7,8,9}; //fixBug: return m -> return minEqIndex
    int a[] = {1,2,2,3,3,4,5,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,9,9,9,9};
    //int a[] = {8}; //fixBug:  minEqIndex == 0
    //int a[] = {9,10};
    int len = sizeof(a)/sizeof(a[0]);

    printf("len : %d
",len);
    for(int i=0;i<len;i++){
        printf("[%d]=%d ",i,a[i]);
    }
    printf("

");

    int m;
    //m = bin_s_first_eq(a,0,len,v,INT_MIN,INT_MAX);
    m = bin_s_first_eq2(a,len,v);
    if(m >= 0)
        printf("find a[%d]=%d
",m,a[m]);
    else
        printf("find m=%d
",m);
}

root@ubuntu:~/cdir# gcc bin_search.c -o bin_search.bin -g
root@ubuntu:~/cdir# ./bin_search.bin
len : 2
[0]=9 [1]=10

a[m] a[1]=10 ,maxLtI:-2147483648,minEqI:2147483647
go left
a[m] a[0]=9 ,maxLtI:-2147483648,minEqI:2147483647
find min:0 max:-2147483648
find m=0

root@ubuntu:~/cdir# gcc bin_search.c -o bin_search.bin -g
root@ubuntu:~/cdir# ./bin_search.bin 
len : 29
[0]=1 [1]=2 [2]=2 [3]=3 [4]=3 [5]=4 [6]=5 [7]=6 [8]=7 [9]=7 [10]=7 [11]=7 [12]=7 [13]=7 [14]=7 [15]=7 [16]=7 [17]=7 [18]=7 [19]=7 [20]=7 [21]=7 [22]=7 [23]=8 [24]=8 [25]=9 [26]=9 [27]=9 [28]=9 

find a[25]=9

2分查找10有9错

//最后一个等于v的
int bs(int[] a,int len, int v){
    int l = 0;
    int r = len-1;
    int m = (l+r)/2;
    while(l<=r){
        m=(l+r)/2;
        if(a[m] < v)
            l=m+1;
        else if(a[m] > v)
            r=m-1;
        else if(m == len-1 || a[m+1] > v)
            return m;
        else
            l=m+1;
            
    }
    return -1;
}


//第一个小于等于v的
int bs_first_lev(int[] a,int len, int v){
    int l=0;
    int r=len-1;
    int m=(l+r)/2;
    while(l<=r){
        m=(l+r)/2;
        if(a[m] >= v)
            if(m == 0 || a[m-1] < v)
                return m;
            else
                r=m-1;
        else
            l=m+1;
    }
    return -1;
}


//第一个大于等于v的
int bs_last_lev(int[] a,int len, int v){
    int l=0;
    int r=len-1;
    int m=(l+r)/2;
    while(l<=r){
        m=(l+r)/2;
        if(a[m] <= v)
            if(m==len-1||a[m+1]>v)
                return m;
            else
                l=m+1;
        else
            r=m-1;
    }
    return -1;
}
一个循环有序数组
456123

如何用二分法找到给定 v值元素?

先找出数组offset
第一个元素向右移动了3个位置,offset = 3
负数取莫得负数的语言中(JAVA)需要转换一下,一下是按照 负数取莫得到正数的逻辑处理的
额外转换逻辑为

正常情况:
A mod B = C         (2%5=2)
-A mod B = B-A         (-2%5=3)

语言特殊情况:
-A mod B = -A 时     (-2%5=-2  java中)
修改 (-A + B) mod B = C     (-2+5 % 5 = 3)

mapIndex = (orgIndex + offset) % arrayLen
orgIndex = (mapIndex - offset) % arrayLen

int m2o(int m, int offset, int len){
    return (mapIndex - offset) % arrayLen;
}

int o2m(int o, int offset, int len){
    return (orgIndex + offset) % arrayLen;
}

int bs(int[] a,int len, int v, int offset){
    int l=o2m(0,offset,len);
    int r=o2m(len,offset,len);
    ...每次用下标的时候都转换,这样缺点是效率低,取模指令类似除法,效率不高
}
原文地址:https://www.cnblogs.com/cyy12/p/11824016.html