二分查找

二分查找

普通二分查找

描述:求解遞增序列array的sta位置到end位置中間滿足等於val值的位置。

適用範圍:有序序列。

優勢:如果數組長度爲n,其算法複雜度爲o(log(n)),每次查找能夠將數組範圍縮小到數組當前長度的一半。

求解:

1.  判斷sta位置是否在end位置後面。

  •  sta<=end,跳轉到步驟2。
  •  sta>end,找不到滿足等於val值的位置,退出循環,返回-1,表示沒有找到。

2.  計算sta位置到end的中間位置mid,mid=(sta+end)/2。計算機採用整數除法,即向下取整。跳轉到步驟3。

3.  比較val與mid位置對應數值的大小。

  • array[mid]>val,則滿足等於val值的位置在sta和mid-1之間,將end賦值爲mid-1,查找空間變爲[sta,mid-1]。因爲(sta+end)/2<=end,所以mid-1一定比end小,空間[sta,end]變爲[sta,mid-1],空間一定縮小。跳轉到步驟1,繼續循環查找。
  • array[mid]<val,則滿足等於val值的位置在mid+1和end之間,將sta賦值爲mid+1,查找空間變爲[mid+1,end]。因爲(sta+end)/2>=sta,所以mid+1一定比sta大,空間[sta,end]變爲[mid+1,end],空間一定縮小。跳轉到步驟1,繼續循環查找。
  • array[mid]=val,則找到滿足等於val值的位置,即mid,退出循環,返回mid值。

Java源碼:

public static int binSearch(int[] array,int val){

    int sta=0;

    int end=array,length-1;

    int mid;

    while(sta<=end){

      //防止sta和end都在很大的時候,兩者之和溢出

        mid=sta+(end-sta)/2;

        if(array[mid]>val)

            end=mid-1;

        else if(array[mid]<val)

            sta=mid+1;

        else

            return mid;

    }

    return -1;

}

測試程序:

public static boolean testBinSearch(int count,int size){

   boolean flag=true;

   int[] array;

   int length;

   int rightResult, computeResult;

   Random random=new Random();

   int errorCount=0;

   array=new int[0];

   rightResult=-1;

   computeResult=binSearch(array,-1);

   if(rightResult!=computeResult){

       PrintError(array,computeResult,rightResult,errorCount++);

       flag=false;

   }

   while(count>0){

       count--;

       length=random.nextInt(size)+1;

       rightResult=random.nextInt(length);

       array=new int[length];

       for(int i=0;i<length;i++){

           if(i==0)

               array[i]=random.nextInt(30);

           else

              array[i]=array[i-1]+random.nextInt(5);

       }

       computeResult=binSearch(array,array[rightResult]);

       if(array[rightResult]!=array[computeResult]){

           PrintError(array,computeResult,rightResult,errorCount++);

           flag=false;

       }

       rightResult=-1;

       computeResult=binSearch(array,-1);

       if(rightResult!=computeResult){

           PrintError(array,computeResult,rightResult,errorCount++);

           flag=false;

       }

   }

   returnflag;

}

測試報告:

執行testBinSearch(1000,100),即測試1000次,測試數組長度爲1到100之間,測試結果正確。


二分查找變型1--查找第一個值等於val的位置

解析:遞增序列array可以同一個值出現多次,求解該問題主要在array[mid]=val時的處理和退出循環判斷條件問題。

1.  當array[mid]=val,滿足第一個值等於val的值一定在sta和mid之間,在sta和mid-1之間如果沒有等於val的值時,mid就是第一個位置,所以此刻範圍只能縮小到sta和mid之間。

2.  退出循環判斷條件問題。array[mid]=val, 則滿足第一個值等於val值的位置在sta和mid之間,查找空間變爲[sta,mid]。當sta=end時,mid與end相等,空間[sta,end]變爲[sta,mid],空間大小沒有改變,會產生死循環。所以循環條件要變爲sta<end.因爲循環條件變爲sta<end,所以在sta=end時沒有和val比較,故退出循環通過比較sta位置的值判斷是否尋找滿足第一個值等於val的值的位置。

Java源碼:

public static int binSearchFirst(int[] array,int val){

    int sta=0;

    int end=array.length-1;

    int mid;

    while(sta<end){

        mid=sta+(end-sta)/2;

        if(array[mid]>val)

            end=mid-1;

        else if(array[mid]<val)

            sta=mid+1;

        else

            end=mid;

    }

    if(array[sta]==val)

        return sta;

    return -1;

}

測試程序:

public static boolean testBinSearchFirst(int count,int size){

    boolean flag=true;

    int[] array;

    int length;

    int rightResult,computeResult;

    Random random=new Random();

    int errorCount=0;

    array=new int[0];

    rightResult=-1;

    computeResult=binSearch(array,-1);

    if(rightResult!=computeResult){

        PrintError(array,computeResult,rightResult,errorCount++);

        flag=false;

    }

    while(count>0){

        count--;

        length=random.nextInt(size)+1;

        rightResult=random.nextInt(length);

        array=new int[length];

        for(int i=0;i<length;i++){

            if(i==0)

                array[i]=random.nextInt(30);

            else if(i==rightResult)

               array[i]=array[i-1]+random.nextInt(5)+1;

            else

               array[i]=array[i-1]+random.nextInt(5);

        }

        computeResult=binSearch(array,array[rightResult]);

        if(array[rightResult]!=array[computeResult]){

            PrintError(array,computeResult,rightResult,errorCount++);

            flag=false;

        }

        rightResult=-1;

        computeResult=binSearch(array,-1);

        if(rightResult!=computeResult){

            PrintError(array,computeResult,rightResult,errorCount++);

            flag=false;

        }

    }

    returnflag;

 }

測試報告:

執行testBinSearchFirst(1000,100),即測試1000次,測試數組長度爲1到100之間,測試結果正確。

 

二分查找變型2--查找最後一個值等於val的位置

解析:求解該問題主要在array[mid]與val的比較處理和退出循環判斷條件問題。

1.  當array[mid]=val,滿足最後一個值等於val的值一定在mid和end之間,在mid+1和end之間如果沒有等於val的值時,mid就是最後一個位置,所以此刻範圍只能縮小到mid和sta之間。

2.  退出循環判斷條件問題。array[mid]=val, 則滿足最後一個值等於val值的位置在mid和end之間,查找空間變爲[mid,end]。當sta=end或者sta=end-1時,(sta+end)/2等於sta,mid與sta相等,空間[sta,end]變爲[mid,end],空間大小沒有改變,會產生死循環。所以循環條件要變爲sta<end-1.因爲循環條件變爲sta<end-1,所以在sta和end-1時沒有和val比較,故退出循環通過比較sta和end-1位置的值判斷是否尋找滿足最後一個值等於val的值的位置。

Java源碼:

public static int binSearchLast(int[] array,int val){

    int sta= 0;

    int end= array.length - 1;

    int mid;

    while (sta< end - 1) {

        mid = sta + (end - sta) / 2;

        if(array[mid] > val)

            end = mid - 1;

        else if(array[mid] < val)

            sta = mid + 1;

        else

            sta = mid;

    }

    if (sta== end - 1 && array[end] == val)

        return end;

    if (sta== end - 1 && array[sta] == val)

        return sta;

    if (sta== end && array[sta] == val)

        return sta;

    return -1;

}

測試程序:

public static boolean testBinSearchLast(int count,intsize) {

    boolean flag= true;

    int[] array;

    int length;

    int rightResult, computeResult;

    Random random = new Random();

    int errorCount = 0;

    array = new int[0];

    rightResult = -1;

    computeResult = binSearchLast(array,-1);

    if(rightResult != computeResult) {

        PrintError(array, computeResult,rightResult, errorCount++);

        flag = false;

    }

    while(count > 0) {

        count--;

        length = random.nextInt(size) + 1;

        rightResult = random.nextInt(length);

        array = new int[length];

        for (int i =0; i < length; i++) {

            if (i== rightResult) {

                // 構建多個相同的值

                intsameLength = random.nextInt(30) + 1;

                int tmp;

                if (i== 0)

                    tmp = random.nextInt(30);

                else

                    tmp = array[i - 1] +random.nextInt(5) + 1;

                while(sameLength > 0 && i < length)

                    array[i++] = tmp;

                if(i<length)

                    array[i] = tmp + 2;

                rightResult = i - 1;

            } else {

                if (i== 0)

                    array[i] =random.nextInt(30);

                else

                    array[i] = array[i - 1] + random.nextInt(5);

            }

        }

        computeResult = binSearchLast(array,array[rightResult]);

        if(rightResult != computeResult) {

            PrintError(array,computeResult, rightResult, errorCount++);

            flag = false;

        }

        rightResult = -1;

        computeResult = binSearchLast(array,-1);

        if(rightResult != computeResult) {

            PrintError(array,computeResult, rightResult, errorCount++);

            flag = false;

        }

    }

    return flag;

}

測試報告:

執行testBinSearchLast(1000,100),即測試1000次,測試數組長度爲1到100之間,測試結果正確。

 

二分查找變型3--查找最接近val並且小於val的位置

解析:求解該問題主要在array[mid]與val比較處理和退出循環判斷條件問題。

1.  array[mid]>val,則滿足最接近val並且小於val值的位置在sta和mid-1之間, 即[sta,mid-1]。繼續循環查找。

2.  array[mid]<val,則滿足等於最接近val並且小於val值的位置在mid和end之間,查找空間變爲[mid,end]。當sta=end或者sta=end-1時,(sta+end)/2等於sta,mid與sta相等,空間[sta,end]變爲[mid,end],空間大小沒有改變,會產生死循環。所以循環條件要變爲sta<end-1.因爲循環條件變爲sta<end-1,所以在sta和end-1時沒有和val比較,故退出循環通過比較sta和end-1位置的值判斷是否尋找滿足最後一個值等於val的值的位置。

3.  array[mid]=val,則滿足等於最接近val並且小於val值的位置在sta和mid-1之間,即[sta,mid-1]。繼續循環查找。

Java源碼:

public static int binSearchLess(int[] array,int val){

    int sta= 0;

    int end= array.length - 1;

    int mid;

    while (sta< end - 1) {

        mid = sta + (end - sta) / 2;

        if(array[mid] > val)

            end = mid - 1;

        else if(array[mid] < val)

            sta = mid;

        else

            end = mid - 1;

    }

    if (sta== end - 1 && array[end] < val)

        return end;

    if (sta== end - 1 && array[sta] < val)

        return sta;

    if (sta== end && array[sta] < val)

        return sta;

    return -1;

}

測試程序:

public static boolean testBinSearchLess(int count,intsize) {

    boolean flag= true;

    int[] array;

    int length;

    int rightResult, computeResult;

    Random random = new Random();

    int errorCount = 0;

    array = new int[0];

    rightResult = -1;

    computeResult = binSearchLess(array,-1);

    if(rightResult != computeResult) {

        PrintError(array, computeResult,rightResult, errorCount++);

        flag = false;

    }

    while(count > 0) {

        count--;

        length = random.nextInt(size) + 1;

        rightResult = random.nextInt(length);

        array = new int[length];

        for (int i =0; i < length; i++) {

            if (i== rightResult) {

                int tmp;

                if (i== 0)

                    tmp = random.nextInt(30);

                else

                    tmp = array[i - 1] +random.nextInt(5) + 1;

                array[i] = tmp;

            } else {

                if (i== 0)

                    array[i] =random.nextInt(30);

                else

                    array[i] = array[i - 1] +random.nextInt(5);

            }

        }

        computeResult = binSearchLess(array,array[rightResult]);

        rightResult--;

        if(rightResult != computeResult) {

            PrintError(array,computeResult, rightResult, errorCount++);

            flag = false;

        }

        rightResult = -1;

        computeResult = binSearchLess(array,-1);

        if(rightResult != computeResult) {

            PrintError(array,computeResult, rightResult, errorCount++);

            flag = false;

        }

        rightResult = array.length-1;

        computeResult = binSearchLess(array,Integer.MAX_VALUE);

        if(rightResult != computeResult) {

            PrintError(array,computeResult, rightResult, errorCount++);

            flag = false;

        }

    }

    returnflag;

}

測試報告:

執行testBinSearchLess(1000,100),即測試1000次,測試數組長度爲1到100之間,測試結果正確。

 

二分查找變型4--查找最接近val並且大於val的位置

解析:遞增序列array可以同一個值出現多次,求解該問題主要在array[mid]和val比較處理和退出循環判斷條件問題。

1.  array[mid]>val,則滿足最接近val並且大於val值的位置在sta和mid之間, 即[sta,mid]。當sta=end時,mid與end相等,空間[sta,end]變爲[sta,mid],空間大小沒有改變,會產生死循環。所以循環條件要變爲sta<end.因爲循環條件變爲sta<end,所以在sta=end時沒有和val比較,故退出循環通過比較sta位置的值判斷是否尋找滿足第一個值等於val的值的位置。

2.  array[mid]<val,則滿足等於最接近val並且大於val值的位置在mid+1和end之間,即[mid+1,end]。繼續循環查找。

3.  array[mid]=val,則滿足等於最接近val並且大於val值的位置在mid+1和end之間,即[mid+1,end]。繼續循環查找。

Java源碼:

public static int binSearchGreater(int[] array,int val){

    int sta= 0;

    int end= array.length - 1;

    int mid;

    while (sta< end) {

        mid = sta + (end - sta) / 2;

        if(array[mid] > val)

            end = mid;

        else if(array[mid] < val)

            sta = mid + 1;

        else

            sta = mid + 1;

    }

    if (sta== end && array[sta] > val)

        return sta;

    return -1;

}

測試程序:

public static boolean testBinSearchGreater(int count,int size) {

    boolean flag= true;

    int[] array;

    int length;

    int rightResult, computeResult;

    Random random = new Random();

    int errorCount = 0;

    array = new int[0];

    rightResult = -1;

    computeResult = binSearchGreater(array,-1);

    if(rightResult != computeResult) {

        PrintError(array, computeResult,rightResult, errorCount++);

        flag = false;

    }

    while(count > 0) {

        count--;

        length = random.nextInt(size) + 1;

        rightResult = random.nextInt(length);

        array = new int[length];

        for (int i =0; i < length; i++) {

            if (i== rightResult) {

                int tmp;

                if (i== 0)

                    tmp = random.nextInt(30);

                else

                    tmp = array[i - 1] +random.nextInt(5) + 1;

                array[i] = tmp;

            } else {

                if (i== 0)

                    array[i] =random.nextInt(30);

                else

                    array[i] = array[i - 1] +random.nextInt(5);

            }

        }

        computeResult = binSearchGreater(array,rightResult==0?-1:array[rightResult-1]);

        if(rightResult != computeResult) {

            PrintError(array,computeResult, rightResult, errorCount++);

            flag = false;

        }

        rightResult = 0;

        computeResult = binSearchGreater(array,-1);

        if(rightResult != computeResult) {

            PrintError(array,computeResult, rightResult, errorCount++);

            flag = false;

        }

        rightResult = -1;

        computeResult = binSearchGreater(array,Integer.MAX_VALUE);

        if(rightResult != computeResult) {

            PrintError(array,computeResult, rightResult, errorCount++);

            flag = false;

        }

    }

    return flag;

}

測試報告:

執行testBinSearchGreater(1000,100),即測試1000次,測試數組長度爲1到100之間,測試結果正確。


下載地址:

http://download.csdn.net/detail/ssuchange/6700749


參考資料:

二分查找之美 http://www.cr173.com/html/20428_1.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章