天天看點

各種二分查找

二分查找

給定一個有序(不降序)數組arr。

1、求任意一個i使得arr[i]等于val,不存在則傳回-1

2、求最小的i使得arr[i]等于val,不存在則傳回-1

3、求最大的i使得arr[i]等于val,不存在則傳回-1

4、求最大的i使得arr[i]小于val,不存在則傳回-1

5、求最小的i使得arr[i]大于val,不存在則傳回-1

一個錯誤的二分查找:

int bisearch(int *arr, int b, int e, int val)
{
    while(b<e)
    { 
        int mid = (b+e)>>2;
        if(arr[mid] <= val)
        {
            b = mid; 
        }
        else
        {
            e = mid-1; 
        } 
    }
    if(arr[e] == val)
    {
        return e; 
    } 
    else
    {
        return -1; 
    } 
}
           

下面通過建構測試用例來找出這個程式的問題

對于所有有效輸入

在arr數組中查找元素val,對于問題123建構如下的測試用例。

arr數組 找到 未找到
空集 不存在 1
1個元素 2 3
多個元素

arr中含有一個val   含有多個

位于arr開頭  4        5

結尾           6        7

中間           8        9

比最小的小            10

比最大的大            11

在範圍内就是不含有    12

建構測試用例

1

int a1[] = {};

cout << bisearch(a, 0, sizeof(a1)/sizeof(int)-1, 8) << endl;

2

int a2[] = {8};

cout << bisearch(a, 0, sizeof(a2)/sizeof(int)-1, 8) << endl;

3

int a3[] = {10};

cout << bisearch(a, 0, sizeof(a3)/sizeof(int)-1, 8) << endl;

4

int a4[] = {1,2,3,4,5,6,7,7,7,7,7};

cout << bisearch(a, 0, sizeof(a4)/sizeof(int)-1, 1) << endl;

5

int a5[] = {1,1,1,2,3,4,5,6,7,7,7,7,7};

cout << bisearch(a, 0, sizeof(a5)/sizeof(int)-1, 1) << endl;

6

int a6[] = {1,2,3,4,5,6,7,7,7,7,7,8};

cout << bisearch(a, 0, sizeof(a6)/sizeof(int)-1, 8) << endl;

7

int a7[] = {1,2,3,4,5,6,7,7,7,7,7,8,8,8,8};

cout << bisearch(a, 0, sizeof(a7)/sizeof(int)-1, 8) << endl;

8

int a8[] = {1,2,3,4,5,6,7,7,7,7,7,8,8,8,10,10,12};

cout << bisearch(a, 0, sizeof(a8)/sizeof(int)-1, 6) << endl;

9

int a9[] = {1,2,3,4,5,6,7,7,7,7,7,8,8,8,10,10,12};

cout << bisearch(a, 0, sizeof(a9)/sizeof(int)-1, 8) << endl;

10

int a10[] = {2,3,4,5,6,7,7,7,7,7};

cout << bisearch(a, 0, sizeof(a10)/sizeof(int)-1, 1) << endl;

11

int a11[] = {2,3,4,5,6,7,7,7,7,7};

cout << bisearch(a, 0, sizeof(a11)/sizeof(int)-1, 12) << endl;

12

int a12[] = {2,3,4,5,7,7,7,7,7};

cout << bisearch(a, 0, sizeof(a12)/sizeof(int)-1, 6) << endl;

期望的測試結果

測試用例編号 問題1 問題2 問題3
1 -1 -1 -1
2
3 -1 -1 -1
4
5 0-2任意 2
6 11 11 11
7 11-14任意 11 14
8 5 5 5
9 11-13 11 13
10 -1 -1 -1
11 -1 -1 -1
12 -1 -1 -1

對于上面那個錯誤程式:測試樣例4進入死循環,經過分析發現,(來自程式設計之美)

各種二分查找

也就是說一種情況下沒有縮小範圍導緻。

對每一種情況都縮小範圍,可以得到題目1的如下代碼:可以通過全部測試樣例

//遞歸
int bisearch(int *arr, int s, int e, int val)
{
    if(s>e) return -1;
    int mid = s + ((e - s) >>1);
    if(arr[mid] == val) return mid;
    else if(arr[mid] < val) return bisearch(arr,mid+1, e, val);
    else return bisearch(arr,s, mid-1, val);
}
// 非遞歸
int bisearch(int *arr, int s, int e, int val)
{
    while(s<=e)
    {
        int mid = s + ((e-s)>>2);
        if(arr[mid]==val) return mid;
        else if(arr[mid]<val)  s=mid+1;
        else e=mid-1; 
    }
    return -1; 
}
//或者
int bisearch(int *arr, int s, int e, int val)
{
    /*
    循環結束有兩種情況
    s為偶數  s=e
    s為奇數  s=e-1 
    */ 
    while(s < e-1)
    {
        int mid = s+((e-s)>>1);
        if(arr[mid]<=val)
        {
            s=mid; 
        }else{
              // 不需要e=mid-1 防止s=e 
            e=mid; 
        } 
    } 
    if(arr[s]==val)  return s;
    if(arr[e]==val)  return e;
    return -1; 
}
           
//第二題:
int bisearch(int *arr, int s, int e, int val)
{
    while(s <= e)
    {
        if(arr[s]==val)  return s; 
        int mid = s+((e-s)>>1);
        if(arr[mid]<val)
        {
            s=mid+1; 
        }else if(arr[mid]==val){
            e=mid; 
        } else{
            e=mid-1; 
        } 
    } 
    return -1; 
}
//第三題:
int bisearch(int *arr, int s, int e, int val)
{
    while(s <= e)
    {
        if(arr[e]==val) return e;
        int mid=s+((e-s-1)>>2)+1; // 盡量往後落,也就是3和4落在4 2和6仍然也落在4 
        if(arr[mid] < val)
            s=mid+1;
        else if(arr[mid] == val) 
            s=mid;
        else 
            e=mid-1; 
    } 
    return -1; 
}
           

對于4,5問題不存在找不到的情況,建構測試用例

arr val
空集 任意1
1個

相等2

大于3

小于 4

多個

小于最小5

等于最小 6

在中間相等7  不等8

等于最大 9

大于最大10

1

int a1[] = {};

cout << bisearch(a1, 0, sizeof(a1)/sizeof(int)-1, 8) << endl;

2

int a2[] = {8};

cout << bisearch(a2, 0, sizeof(a2)/sizeof(int)-1, 8) << endl;

3

int a3[] = {10};

cout << bisearch(a3, 0, sizeof(a3)/sizeof(int)-1, 8) << endl;

4

int a4[] = {5};

cout << bisearch(a4, 0, sizeof(a4)/sizeof(int)-1, 8) << endl;

5

int a5[] = {3,5,6,7,7,7,8,8,8,8,12,18};

cout << bisearch(a5, 0, sizeof(a5)/sizeof(int)-1, 1) << endl;

6

int a6[] = {3,5,6,7,7,7,8,8,8,8,12,18};

cout << bisearch(a6, 0, sizeof(a6)/sizeof(int)-1, 3) << endl;

7

int a7[] = {3,5,6,7,7,7,8,8,8,8,12,18};

cout << bisearch(a7, 0, sizeof(a7)/sizeof(int)-1, 7) << endl;

8

int a8[] = {3,5,6,7,7,7,8,8,8,8,12,18};

cout << bisearch(a8, 0, sizeof(a8)/sizeof(int)-1, 9) << endl;

9

int a9[] = {3,5,6,7,7,7,8,8,8,8,12,18};

cout << bisearch(a9, 0, sizeof(a9)/sizeof(int)-1, 18) << endl;

10

int a10[] = {3,5,6,7,7,7,8,8,8,8,12,18};

cout << bisearch(a10, 0, sizeof(a10)/sizeof(int)-1, 20) << endl;

測試用例編号 問題4 問題5
1 -1 -1
2 -1 -1
3 -1
4 -1
5 -1
6 -1 1
7 2 6
8 9 10
9 10 -1
10 11 -1
//第四題
int bisearch(int *arr, int s, int e, int val)
{
    //求最大的i使得arr[i]小于val,不存在則傳回-1
    while(s<=e){
        if(arr[e]<val) return e; 
        int mid = s+((e-s-1)>>1)+1;
        if(arr[mid]<val){
            s=mid; 
        } 
        else{
             e=mid-1; 
        }            
    } 
    return -1; 
}
//第五題
int bisearch(int *arr, int s, int e, int val)
{
    //求最小的i使得arr[i]大于val,不存在則傳回-1
    while(s<=e){
        if(arr[s] > val) return s; 
        int mid = s + ((e-s)>>1); 
        if(arr[mid]<=val){
            s=mid+1; 
        } else{
            e=mid; 
        } 
    } 
    return -1; 
}