C++ 二分搜

搜尋(Search)

如何在一群資料裡面查找某個資料 ?

線性搜尋(Linear search): 從頭到尾掃描一次

int target = 9, idx;
int a[] = {5, 4, 9, 1, 2};
for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
	if(a[i] == target)
		idx = i;
}
cout << idx << "\n";

Binary search

What if the data is sorted ?

Binary search

對於每個在陣列裡的數,他不是大於等於target,就是小於target

Binary search

對於每個在陣列裡的數,他不是大於等於target,就是小於target

設target=38

data=[2, 11, 24, 35, 38, 39, 44, 47, 51, 55, 72, 75, 82, 85, 91, 96]

[2, 11, 24, 35, 38, 39, 44, 47, 51, 55, 72, 75, 82, 85, 91, 96]

[2, 11, 24, 35, 38, 39, 44, 47, 51, 55, 72, 75, 82, 85, 91, 96]

[2, 11, 24, 35, 38, 39, 44, 47, 51, 55, 72, 75, 82, 85, 91, 96]

[2, 11, 24, 35, 38, 39, 44, 47, 51, 55, 72, 75, 82, 85, 91, 96]

 [2, 11, 24, 35, 38, 39, 44, 47, 51, 55, 72, 75, 82, 85, 91, 96]

 [2, 11, 24, 35, 38, 39, 44, 47, 51, 55, 72, 75, 82, 85, 91, 96]

 [2, 11, 24, 35, 38, 39, 44, 47, 51, 55, 72, 75, 82, 85, 91, 96]

Steps

  1. 從區間的中點開始比較。

  2. 如果目標值等於中點,搜尋結束。

  3. 如果目標值小於中點,則縮小到左半邊繼續找;否則縮小到右半邊。

  4. 每次都把搜尋範圍縮小一半,直到找到或區間為空。

Remark:  我們是在尋找index,透過index可以拿到資料

target 要在範圍裡面

int binary_search(int a[], int target, int n) {
    int l = 0, r = n;  // [l, r)
    while (l < r) {
        int mid = (l + r) / 2;
        if (a[mid] == target) return mid;
        else if (a[mid] < target) l = mid + 1; 
        // 丟掉 [l, mid]
        else r = mid;                          
        // 丟掉 [mid, r)
    }
    return -1;
}

Binary search

二分搜常見錯誤

  • 開閉區間沒定義好
  • mid 沒有寫對導致target沒有出現在區間裡面

左閉右閉 [l, r]

int binary_search(int a[], int target, int n) {
    int l = 0, r = n - 1; // [0, n - 1] 都在陣列裡面
    while (l < r) { 
        int mid = (l + r) / 2;
        if (a[mid] == target) 
        	return mid; // 找到
        else if (a[mid] < target) 
        	l = mid + 1;
        else 
        	r = mid - 1;
    }
    return -1; // 沒找到
}

Remark: target一定要在 [l, r] 裡面

左閉右開 [l, r)

int binary_search(int a[], int target, int n) {
    int l = 0, r = n; // n沒有在陣列裡面
    while (l < r) { 
        cout << l <<" " << r <<"\n";
        int mid = (l + r) / 2;
        if (a[mid] < target)
            l = mid + 1; //注意是mid + 1
        else
            r = mid;
    }
    return l;
}

Remark: target一定要存在 [l, r) 裡面

當 l == r時 區間已經空了,不用再檢查

Upper_bound

Upper_bound: 第一個大於 target 的元素位置 a[idx] > target
target: 6

array: {1, 1, 2, 3, 5, 5, 6, 6, 7, 9, 13, 15}

idx: 8

嘗試用左閉右開寫出upper_bound

Upper_bound

goal: 
find idx such that a[idx] > target
Case 1: a[mid] <= target

[l, mid] 不會是答案,
答案在[mid + 1, r)裡面

Case 2: a[mid] > target

答案是mid 或是在 [l, mid) 裡面

Upper_bound

int upper_bound(int a[], int target, int n) {
    int l = 0, r = n; // [0, n) n沒有在陣列裡面
    while (l < r) { 
        int mid = (l + r) / 2;
        if(a[mid] <= target)
            l = mid + 1;
        else
            r = mid;
    }
    return l; //最後l會等於r
}

Lower_bound

Lower_bound: 第一個大於等於 target 的元素位置 a[idx] >= target
target: 6

array: {1, 1, 2, 3, 5, 5, 6, 6, 7, 9, 13, 15}

idx: 6

嘗試用左閉右開寫出lower_bound

lower_bound

goal: 
find idx such that a[idx] >= target
Case 1: a[mid] < target

[l, mid] 不會是答案,
答案在[mid + 1, r)裡面

Case 2: a[mid] >= target

答案是mid 或是在 [l, mid) 裡面

lower_bound

int lower_bound(int a[], int target, int n) {
    int l = 0, r = n; // [0, n) n沒有在陣列裡面
    while (l < r) { 
        int mid = (l + r) / 2;
        if(a[mid] < target)
            l = mid + 1; // mid 太小,丟掉左半
        else
            r = mid;  
       // mid >= target,可能是答案,縮小右邊界
    }
    return l; 
    // l == r,答案是第一個 >= target 的位置
}

練習

C++ 二分搜

By ernestii26