diff --git a/oficina-busca-binaria/binary-search.md b/oficina-busca-binaria/binary-search.md new file mode 100644 index 0000000000000000000000000000000000000000..9e13762cfa97df66746c508b64f5a9a4d4c22e96 --- /dev/null +++ b/oficina-busca-binaria/binary-search.md @@ -0,0 +1,132 @@ +# Busca Binária e suas diferentes implementações + +Implementação com intervalo [0, n-1] +```cpp +int lo = 0; +int hi = n - 1; +while (l <= r) { + int mid = lo + (hi - lo) / 2; + if (a[mid] == target) { + return mid; + } else if (a[mid] > target) { + hi = mid - 1; + } else { + lo = mid + 1; + } +} + +return -1; +``` + +Implementação com intervalo [0, n) +```cpp +int lo = 0; +int hi = n; +while (l < r) { + int mid = lo + (hi - lo) / 2; + if (a[mid] == target) { + return mid; + } else if (a[mid] > target) { + hi = mid; + } else { + lo = mid + 1; + } +} + +return -1; +``` + +Implementação com intervalo [0, n-1] e verificação no fim. Requer na média uma +iteração a mais. +```cpp +int lo = 0; +int hi = n - 1; +while (l < r) { + int mid = lo + (hi - lo) / 2; + if (a[mid] > target) { + hi = mid + 1; + } else { + lo = mid; + } +} + +if (a[lo] == target) { + return lo; +} else { + return -1; +} +``` + +Implementação com intervalo [0, n) para achar o elemento mais a esquerda. +```cpp +int lo = 0; +int hi = n; +while (l < r) { + int mid = lo + (hi - lo) / 2; + if (a[mid] >= target) { + hi = mid; + } else { + lo = mid + 1; + } +} + +return lo; +``` + +Implementação com intervalo [0, n-1] para achar o elemento mais a esquerda. +```cpp +int lo = 0; +int hi = n - 1; +while (l <= r) { + int mid = lo + (hi - lo) / 2; + if (a[mid] >= target) { + hi = mid - 1; + } else { + lo = mid + 1; + } +} + +return lo; +``` + +Implementação com intervalo [0, n-1] para achar o elemento mais a esquerda. Errichto. +```cpp +int lo = 0; +int hi = n - 1; +int ans = -1; +while (l <= r) { + int mid = lo + (hi - lo) / 2; + if (a[mid] >= target) { + ans = mid; + hi = mid - 1; + } else { + lo = mid + 1; + } +} + +return ans; +``` + +Implementação com intervalo [0, n) para achar o elemento mais a direita. +```cpp +int lo = 0; +int hi = n; +while (l < r) { + int mid = lo + (hi - lo) / 2; + if (a[mid] > target) { + hi = mid; + } else { + lo = mid + 1; + } +} + +return hi - 1; +``` + +# Tipos de busca binária +* Achar elemento exato +* Achar o primeiro elemento com alguma propriedade +* Achar o último elemento com alguma propriedade + +# Como formular um problema de busca binária +Pensar em um array particionado em verdadeiro e falso. diff --git a/oficina-busca-binaria/main.cpp b/oficina-busca-binaria/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..440ec355ceb59218740b14e3d6c94a6cffbc38c3 --- /dev/null +++ b/oficina-busca-binaria/main.cpp @@ -0,0 +1,595 @@ +#include <bits/stdc++.h> +using namespace std; + +int comp; + +// Buscas +int busca_intervalo_0_n_1 (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] == target) { + return mid; + } else if (a[mid] > target) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } + + return -1; +} + +int busca_intervalo_0_n (vector<int> a, int n, int target) { + int lo = 0; + int hi = n; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] == target) { + return mid; + } else if (a[mid] > target) { + hi = mid; + } else { + lo = mid + 1; + } + } + + return -1; +} + +int busca_intervalo_0_n_end (vector<int> a, int n, int target) { + int lo = 0; + int hi = n; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] >= target) { + hi = mid; + } else { + lo = mid + 1; + } + } + + if (n > 0) comp++; + return n > 0 && a[lo] == target ? lo : -1; +} + +int busca_intervalo__1_n (vector<int> a, int n, int target) { + int lo = -1; + int hi = n; + while (lo < hi - 1) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] == target) { + return mid; + } else if (a[mid] > target) { + hi = mid; + } else { + lo = mid; + } + } + + return -1; +} + +int busca_intervalo_0_n_1_ceil_end (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo < hi) { + int mid = lo + (hi - lo + 1) / 2; + comp++; + if (a[mid] > target) { + hi = mid - 1; + } else { + lo = mid; + } + } + + if (n > 0) comp++; + return n > 0 && a[lo] == target ? lo : -1; +} + +int esquerda_intervalo_0_n_lo (vector<int> a, int n, int target) { + int lo = 0; + int hi = n; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] >= target) { + hi = mid; + } else { + lo = mid + 1; + } + } + + return lo; +} + +int esquerda_intervalo_0_n_hi (vector<int> a, int n, int target) { + int lo = 0; int hi = n; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] >= target) { + hi = mid; + } else { + lo = mid + 1; + } + } + + return hi; +} + +int esquerda_intervalo__1_n_lo (vector<int> a, int n, int target) { + int lo = -1; + int hi = n; + while (lo < hi - 1) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] >= target) { + hi = mid; + } else { + lo = mid; + } + } + + return lo+1; +} + +int esquerda_intervalo__1_n_hi (vector<int> a, int n, int target) { + int lo = -1; + int hi = n; + while (lo < hi - 1) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] >= target) { + hi = mid; + } else { + lo = mid; + } + } + + return hi; +} + +int esquerda_intervalo_0_n_1_ceil_lo (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo < hi) { + int mid = lo + (hi - lo + 1) / 2; + comp++; + if (a[mid] >= target) { + hi = mid - 1; + } else { + lo = mid; + } + } + + comp++; + return a[lo] >= target ? lo : lo + 1; +} + +int esquerda_intervalo_0_n_1_lo (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] >= target) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } + + return lo; +} + +int esquerda_intervalo_0_n_1_ceil_hi (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo < hi) { + int mid = lo + (hi - lo + 1) / 2; + comp++; + if (a[mid] >= target) { + hi = mid - 1; + } else { + lo = mid; + } + } + + return a[hi] >= target ? hi : hi + 1; +} + +int esquerda_intervalo_0_n_1_hi (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] >= target) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } + + return hi + 1; +} + +int esquerda_intervalo_0_n_1_aux (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + int ans = n; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] >= target) { + ans = mid; + hi = mid - 1; + } else { + lo = mid + 1; + } + } + + return ans; +} + +int esquerda_lower_bound (vector<int> a, int n, int target) { + return lower_bound(a.begin(), a.begin() + n, target) - a.begin(); +} + +int direita_intervalo_0_n_lo (vector<int> a, int n, int target) { + int lo = 0; + int hi = n; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] > target) { + hi = mid; + } else { + lo = mid + 1; + } + } + + return lo; +} + +int direita_intervalo_0_n_hi (vector<int> a, int n, int target) { + int lo = 0; + int hi = n; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] > target) { + hi = mid; + } else { + lo = mid + 1; + } + } + + return hi; +} + +int direita_intervalo__1_n_lo (vector<int> a, int n, int target) { + int lo = -1; + int hi = n; + while (lo < hi - 1) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] > target) { + hi = mid; + } else { + lo = mid; + } + } + + return lo + 1; +} + +int direita_intervalo__1_n_hi (vector<int> a, int n, int target) { + int lo = -1; + int hi = n; + while (lo < hi - 1) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] > target) { + hi = mid; + } else { + lo = mid; + } + } + + return lo + 1; +} + +int direita_intervalo_0_n_1_lo (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] > target) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } + + return lo; +} + +int direita_intervalo_0_n_1_ceil_lo (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo < hi) { + int mid = lo + (hi - lo + 1) / 2; + comp++; + if (a[mid] > target) { + hi = mid - 1; + } else { + lo = mid; + } + } + + comp++; + return a[lo] <= target ? lo + 1 : lo; +} + +int direita_intervalo_0_n_1_ceil_hi (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo < hi) { + int mid = (lo + hi + 1) / 2; + comp++; + if (a[mid] > target) { + hi = mid - 1; + } else { + lo = mid; + } + } + + comp++; + return a[hi] <= target ? hi + 1 : hi; +} + +int direita_intervalo_0_n_1_hi (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] > target) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } + + return hi + 1; +} + +int direita_intervalo_0_n_1_aux (vector<int> a, int n, int target) { + int lo = 0; + int hi = n - 1; + int ans = n; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + comp++; + if (a[mid] > target) { + ans = mid; + hi = mid - 1; + } else { + lo = mid + 1; + } + } + + return ans; +} + +int direita_upper_bound (vector<int> a, int n, int target) { + return upper_bound(a.begin(), a.begin() + n, target) - a.begin(); +} + +typedef function<int(vector<int>, int, int)> binary_search_function; +typedef function<string(binary_search_function)> test_binary_search_function; + +string not_found_if_array_is_empty(binary_search_function impl) { + return impl({}, 0, 1) != -1 ? "not -1" : ""; +} + +string found_if_in_odd_array(binary_search_function impl) { + vector<int> a { 0, 2, 4, 6, 8, 10, 12, 14, 16 }; + for (int i = 0; i < 9; i++) if (impl(a, 9, a[i]) == -1) { return "false"; } + return ""; +} + +string found_if_in_even_array(binary_search_function impl) { + vector<int> a { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 }; + for (int i = 0; i < 10; i++) if (impl(a, 10, a[i]) == -1) { return "false"; } + return ""; +} + +string found_left_in_odd_array(binary_search_function impl) { + vector<int> a { 2, 2, 4, 4, 4, 6, 8, 8, 10 }; + vector<int> r { 0, 0, 0, 2, 2, 5, 5, 6, 6, 8, 8, 9 }; + for (int i = 0; i < 12; i++) if (impl(a, 9, i) != r[i]) { + return "esperava encontrar item " + to_string(i) + " na posição " + to_string(r[i]) + " mas obtive " + to_string(impl(a, 9, i)); + } + return ""; +} + +string found_left_in_even_array(binary_search_function impl) { + vector<int> a { 2, 2, 4, 4, 4, 6, 8, 8, 10, 10 }; + vector<int> r { 0, 0, 0, 2, 2, 5, 5, 6, 6, 8, 8, 10, 10 }; + for (int i = 0; i < 13; i++) if (impl(a, 10, i) != r[i]) { + return "esperava encontrar item " + to_string(i) + " na posição " + to_string(r[i]) + " mas obtive " + to_string(impl(a, 10, i)); + } + return ""; +} + +string found_right_in_odd_array(binary_search_function impl) { + vector<int> a { 2, 2, 4, 4, 6, 6, 8, 8, 10 }; + vector<int> r { 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10 }; + for (int i = 0; i < 10; i++) if (impl(a, 9, i) != r[i]) { + return "esperava encontrar item " + to_string(i) + " na posição " + to_string(r[i]) + " mas obtive " + to_string(impl(a, 9, i)); + } + return ""; +} + +string found_right_in_even_array(binary_search_function impl) { + vector<int> a { 2, 2, 4, 4, 6, 6, 8, 8, 10, 10 }; + vector<int> r { 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10 }; + for (int i = 0; i < 11; i++) if (impl(a, 10, i) != r[i]) { + return "esperava encontrar item " + to_string(i) + " na posição " + to_string(r[i]) + " mas obtive " + to_string(impl(a, 10, i)); + } + return ""; +} + +string not_found_if_not_found_in_odd_array(binary_search_function impl) { + vector<int> a { 0, 2, 4, 6, 8, 10, 12, 14, 16 }; + for (int i = 0; i < 9; i++) if (impl(a, 9, a[i]+1) != -1) { + return "esperava não encontrar item " + to_string(a[i]+1) + " mas obtive " + to_string(impl(a, 9, a[i]+1)); + } + return ""; +} + +string not_found_if_not_found_in_even_array(binary_search_function impl) { + vector<int> a { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 }; + for (int i = 0; i < 10; i++) if (impl(a, 10, a[i]+1) != -1) { + return "esperava não encontrar item " + to_string(a[i]+1) + " mas obtive " + to_string(impl(a, 10, a[i]+1)); + } + return ""; +} + +void perform_tests( + vector<pair<string, binary_search_function>> implementations, + vector<pair<string, test_binary_search_function>> tests +) { + for (auto test : tests) { + string test_name; + test_binary_search_function test_function; + tie(test_name, test_function) = test; + + cout << " Teste '" << test_name << "'\n"; + for (auto implementation : implementations) { + string implementation_name; + binary_search_function search_function; + tie(implementation_name, search_function) = implementation; + + comp = 0; + string success = test_function(search_function); + cout << " " << (success == "" ? "\033[32mAC" : "\033[91mWA") << " " << implementation_name; + if (success != "") cout << ": " << success; + cout << " : " << comp << " comparações" << "\033[0m\n"; + } + } +} + +int main() { + cout << "Buscas" << "\n"; + + vector<pair<string, binary_search_function>> searches { + make_pair("Busca: Intervalo [0, n-1]", busca_intervalo_0_n_1), + make_pair("Busca: Intervalo [0, n-1]", busca_intervalo_0_n_1), + make_pair("Busca: Intervalo [0, n)", busca_intervalo_0_n), + make_pair("Busca: Intervalo [0, n), verificação no fim", + busca_intervalo_0_n_end), + make_pair("Busca: Intervalo (-1, n)", busca_intervalo__1_n), + make_pair("Busca: Intervalo [0, n-1], teto, verificação no fim", + busca_intervalo_0_n_1_ceil_end), + }; + vector<pair<string, test_binary_search_function>> search_tests { + make_pair("Vetor vazio", + not_found_if_array_is_empty), + make_pair("Elemento não encontrado em vetor ímpar", + not_found_if_not_found_in_odd_array), + make_pair("Elemento não encontrado em vetor par", + not_found_if_not_found_in_even_array), + make_pair("Elemento encontrado em vetor ímpar", + found_if_in_odd_array), + make_pair("Elemento encontrado em vetor par", + found_if_in_even_array), + }; + + perform_tests(searches, search_tests); + + cout << "Primeiro elemento a esquerda" << "\n"; + + vector<pair<string, binary_search_function>> lefts { + make_pair("Esquerda: Intervalo [0, n), resposta lo", + esquerda_intervalo_0_n_lo), + make_pair("Esquerda: Intervalo [0, n), resposta hi", + esquerda_intervalo_0_n_hi), + make_pair("Esquerda: Intervalo [0, n-1], resposta lo", + esquerda_intervalo_0_n_1_lo), + make_pair("Esquerda: Intervalo [0, n-1], resposta hi", + esquerda_intervalo_0_n_1_hi), + make_pair("Esquerda: Intervalo [0, n-1], variável auxiliar", + esquerda_intervalo_0_n_1_aux), + make_pair("Esquerda: Intervalo [0, n-1], teto, resposta lo", + esquerda_intervalo_0_n_1_ceil_lo), + make_pair("Esquerda: Intervalo [0, n-1], teto, resposta hi", + esquerda_intervalo_0_n_1_ceil_hi), + make_pair("Esquerda: Intervalo [0, n-1], teto, variável auxiliar", + esquerda_intervalo__1_n_lo), + make_pair("Esquerda: Intervalo (-1, n), resposta hi", + esquerda_intervalo__1_n_lo), + make_pair("Esquerda: lower_bound", + esquerda_lower_bound), + }; + + vector<pair<string, test_binary_search_function>> left_tests { + make_pair("Elemento mais a esquerda em vetor ímpar", + found_left_in_odd_array), + make_pair("Elemento mais a esquerda em vetor par", + found_left_in_even_array), + make_pair("Elemento encontrado em vetor ímpar", + found_if_in_odd_array), + make_pair("Elemento encontrado em vetor par", + found_if_in_even_array), + }; + + perform_tests(lefts, left_tests); + + cout << "Primeiro elemento a direita" << "\n"; + + vector<pair<string, binary_search_function>> rights { + make_pair("Direita: Intervalo [0, n), resposta lo", + direita_intervalo_0_n_lo), + make_pair("Direita: Intervalo [0, n), resposta hi", + direita_intervalo_0_n_hi), + make_pair("Direita: Intervalo [0, n-1], resposta lo", + direita_intervalo_0_n_1_lo), + make_pair("Direita: Intervalo [0, n-1], resposta hi", + direita_intervalo_0_n_1_hi), + make_pair("Direita: Intervalo [0, n-1], variável auxiliar", + direita_intervalo_0_n_1_aux), + make_pair("Direita: Intervalo [0, n-1], teto, resposta lo", + direita_intervalo_0_n_1_ceil_lo), + make_pair("Direita: Intervalo [0, n-1], teto, resposta hi", + direita_intervalo_0_n_1_ceil_hi), + make_pair("Direita: Intervalo [0, n-1], teto, variável auxiliar", + direita_intervalo__1_n_lo), + make_pair("Direita: Intervalo (-1, n), resposta hi", + direita_intervalo__1_n_hi), + make_pair("Direita: upper_bound", + direita_upper_bound), + }; + + vector<pair<string, test_binary_search_function>> right_tests { + make_pair("Elemento mais a direita em vetor ímpar", + found_right_in_odd_array), + make_pair("Elemento mais a direita em vetor par", + found_right_in_even_array), + make_pair("Elemento encontrado em vetor ímpar", + found_if_in_odd_array), + make_pair("Elemento encontrado em vetor par", + found_if_in_even_array), + }; + + perform_tests(rights, right_tests); +}