Binary Search
二分搜尋法
有序資料
Ordered
有序資料
- 如果有一些陣列,滿足前一個元素與後一個元素,都滿足小於的關係,那這一個陣列就是一個有序資料
1 | 2 | 3 | 5 | 6 |
---|
20 | 30 | 87 | 99 | 256 |
---|
有序資料
- 我們可以定義一個符號 \(<_+\) 來表示順序的關係
- 如果一個序列 \(\{a_i\}\) 的任兩個元素 \(a_i, a_j\)滿足
- \(i<j\)
- \(a_i<_+a_j\)
序列 \(\{a_i\}\) 就是一個有序資料
有序資料 Example
- 排序完的期中考成績
- 排序完的學號
- 排序完的菜單價錢
- 排序完的XXX
搜尋問題
Search
搜尋問題
- 給大家的成績,問:
- 有多少人考100分
- 有沒有人考87分
- 有多少人不及格
搜尋問題
有數列\(a_1,a_2,a_3,\cdots,a_n\),想要做一些事
- 問有沒有一個數字 \(a_i\) 等於 \(x\)
Liner Search
- 把所有可能的資料都看過一次
Liner Search
- 把所有可能的資料都看過一次
int a[5] = {55, 60, 70, 75, 99};
int x = 100;
int index = -1;
for(int i=0; i<5; ++i)
{
if( a[i] == x )
index = i;
}
printf("data at %d", index);
Liner Search
- 把所有可能的資料都看過一次
- 故如果有 \(N\) 筆資料,那就必須看完所有的資料
- 需要花費 \(O(N)\) 的時間
好像有點浪費時間?如果資料有神奇的特性呢?
有序資料的搜尋
有序資料的搜尋
- 如果今天資料是有序的,我們想要在資料中找 \(x\),那我們可以把資料分成三種
小於x | 等於x | 大於x |
---|
\(<_+\)
\(<_+\)
有序資料的搜尋
- 如果在資料中,隨意地戳一個元素來看,因為資料是已序的,我們就可以知道是戳到哪一團
\(<_+\)
\(<_+\)
1 | 3 | 5 | 6 | 7 | 8 | 10 | 12 | 13 |
---|
小於6
大於6
有序資料的搜尋
- 如果在資料中,隨意地戳一個元素來看,因為資料是已序的,我們就可以知道是戳到哪一團
\(<_+\)
\(<_+\)
1 | 3 | 5 | 6 | 7 | 8 | 10 | 12 | 13 |
---|
小於6
大於6
有序資料的搜尋
- 如果在資料中,隨意地戳一個元素來看,因為資料是已序的,我們就可以知道是戳到哪一團
\(<_+\)
\(<_+\)
1 | 3 | 5 | 6 | 7 | 8 | 10 | 12 | 13 |
---|
小於6
大於6
有序資料的搜尋
- 要怎麼戳效率最好呢?
二分搜尋法
- 每一次都戳最中間的元素,這樣每一次一定會少掉一半的資料。
二分搜尋法
- 每一次都戳最中間的元素,這樣每一次一定會少掉一半的資料。
1 | 3 | 5 | 6 | 7 | 8 | 10 | 12 | 13 |
---|
二分搜尋法
- 每一次都戳最中間的元素,這樣每一次一定會少掉一半的資料。
1 | 3 | 5 | 6 | 7 | 8 | 10 | 12 | 13 |
---|
二分搜尋法
- 每一次都戳最中間的元素,這樣每一次一定會少掉一半的資料。
1 | 3 | 5 | 6 | 7 | 8 | 10 | 12 | 13 |
---|
二分搜尋法
- 每一次都戳最中間的元素,這樣每一次一定會少掉一半的資料。
1 | 3 | 5 | 6 | 7 | 8 | 10 | 12 | 13 |
---|
二分搜尋法
int a[100], x;
int L = 0; // 資料的最左邊
int R = N-1; // 資料的最右邊
int index = -1; // 找到的位置
二分搜尋法
int a[100], x;
int L = 0; // 資料的最左邊
int R = N-1; // 資料的最右邊
int index = -1; // 找到的位置
while( L<=R ) // 在範圍有東西的狀況下
{
}
二分搜尋法
int a[100], x;
int L = 0; // 資料的最左邊
int R = N-1; // 資料的最右邊
int index = -1; // 找到的位置
while( L<=R ) // 在範圍有東西的狀況下
{
int M = (L+R)/2; //中間點
}
二分搜尋法
int a[100], x;
int L = 0; // 資料的最左邊
int R = N-1; // 資料的最右邊
int index = -1; // 找到的位置
while( L<=R ) // 在範圍有東西的狀況下
{
int M = (L+R)/2; //中間點
if( a[M] < x ) L=M+1; //戳到左邊,比 M 小的都不是答案
}
二分搜尋法
int a[100], x;
int L = 0; // 資料的最左邊
int R = N-1; // 資料的最右邊
int index = -1; // 找到的位置
while( L<=R ) // 在範圍有東西的狀況下
{
int M = (L+R)/2; //中間點
if( a[M] < x ) L=M+1; //戳到左邊,比 M 小的都不是答案
else if( x < a[M] ) R=M-1; //戳到右邊,比 M 大的都不是答案
}
二分搜尋法
int a[100], x;
int L = 0; // 資料的最左邊
int R = N-1; // 資料的最右邊
int index = -1; // 找到的位置
while( L<=R ) // 在範圍有東西的狀況下
{
int M = (L+R)/2; //中間點
if( a[M] < x ) L=M+1; //戳到左邊,比 M 小的都不是答案
else if( x < a[M] ) R=M-1; //戳到右邊,比 M 大的都不是答案
else { index = M; break; } //找到答案!
}
二分搜尋法
- 因為每一次的搜尋,都會刪掉一半的資料
- 二分搜尋法可以在\(O(\log{N})\)的時間查詢已序資料
基礎程式設計與競技程式設計的差異
int a[100], x;
int L = 0; // 資料的最左邊
int R = N-1; // 資料的最右邊
int index = -1; // 找到的位置
while( L<=R ) // 在範圍有東西的狀況下
{
int M = (L+R)/2; //中間點
if( a[M] < x ) L=M+1; //戳到左邊,比 M 小的都不是答案
else if( x < a[M] ) R=M-1; //戳到右邊,比 M 大的都不是答案
else { index = M; break; } //找到答案!
}
AC
WA / TLE
這份 Code 有一個 bug (while迴圈內)
注意overflow
int M = (L+R)/2;
L = 2147483646, R = 2147483647
可改寫為
int M = L+(R-L)/2;
binary_search-1
By sylveon
binary_search-1
- 623