diff --git a/searches/simple_binary_search.py b/searches/simple_binary_search.py index 00e83ff9e4a3..895434d7473d 100644 --- a/searches/simple_binary_search.py +++ b/searches/simple_binary_search.py @@ -11,50 +11,61 @@ from __future__ import annotations -def binary_search(a_list: list[int], item: int) -> bool: +def binary_search(a_list: list[int], item: int) -> int: """ + Returns the leftmost index of `item` in `a_list` if found, + otherwise returns -1. + >>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] >>> binary_search(test_list, 3) - False + -1 >>> binary_search(test_list, 13) - True + 4 >>> binary_search([4, 4, 5, 6, 7], 4) - True + 0 >>> binary_search([4, 4, 5, 6, 7], -10) - False + -1 >>> binary_search([-18, 2], -18) - True + 0 >>> binary_search([5], 5) - True + 0 >>> binary_search(['a', 'c', 'd'], 'c') - True + 1 >>> binary_search(['a', 'c', 'd'], 'f') - False + -1 >>> binary_search([], 1) - False + -1 >>> binary_search([-.1, .1 , .8], .1) - True + 1 >>> binary_search(range(-5000, 5000, 10), 80) - True + 508 >>> binary_search(range(-5000, 5000, 10), 1255) - False + -1 >>> binary_search(range(0, 10000, 5), 2) - False + -1 """ - if len(a_list) == 0: - return False - midpoint = len(a_list) // 2 - if a_list[midpoint] == item: - return True - if item < a_list[midpoint]: - return binary_search(a_list[:midpoint], item) - else: - return binary_search(a_list[midpoint + 1 :], item) + low, high = 0, len(a_list) - 1 + result = -1 + + while low <= high: + mid = (low + high) // 2 + if a_list[mid] == item: + result = mid + high = mid - 1 # keep searching left for duplicates + elif item < a_list[mid]: + high = mid - 1 + else: + low = mid + 1 + + return result if __name__ == "__main__": user_input = input("Enter numbers separated by comma:\n").strip() sequence = [int(item.strip()) for item in user_input.split(",")] target = int(input("Enter the number to be found in the list:\n").strip()) - not_str = "" if binary_search(sequence, target) else "not " - print(f"{target} was {not_str}found in {sequence}") + index = binary_search(sequence, target) + if index != -1: + print(f"{target} found at index {index} in {sequence}") + else: + print(f"{target} was not found in {sequence}") diff --git a/searches/tests/__init__.py b/searches/tests/__init__.py new file mode 100644 index 000000000000..d225bc65ea3f Binary files /dev/null and b/searches/tests/__init__.py differ diff --git a/searches/tests/test_binary_search_leftmost.py b/searches/tests/test_binary_search_leftmost.py new file mode 100644 index 000000000000..fd157041879a --- /dev/null +++ b/searches/tests/test_binary_search_leftmost.py @@ -0,0 +1,17 @@ +from searches.simple_binary_search import binary_search + + +def test_binary_search_leftmost_duplicate(): + assert binary_search([1, 2, 2, 2, 3], 2) == 1 + + +def test_binary_search_all_duplicates(): + assert binary_search([1, 1, 1, 1], 1) == 0 + + +def test_binary_search_not_found(): + assert binary_search([1, 2, 3], 4) == -1 + + +def test_binary_search_single_element(): + assert binary_search([5], 5) == 0 diff --git a/solution.patch b/solution.patch new file mode 100644 index 000000000000..2987e6ff7e8f Binary files /dev/null and b/solution.patch differ diff --git a/test.patch b/test.patch new file mode 100644 index 000000000000..2f806fbc3704 Binary files /dev/null and b/test.patch differ diff --git a/test.sh b/test.sh new file mode 100755 index 000000000000..0dac8f63d6e9 --- /dev/null +++ b/test.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -uo pipefail +cd /app + +OUTPUT_PATH="" +MODE="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --output_path) + OUTPUT_PATH="$2" + shift 2 + ;; + base|new) + MODE="$1" + shift + ;; + *) + echo "unknown argument: $1" >&2 + exit 2 + ;; + esac +done + +if [[ -z "$MODE" ]]; then + echo "mode argument required (base or new)" >&2 + exit 2 +fi + +case "$MODE" in + base) + pytest tests --ignore=tests/test_binary_search_leftmost.py --junitxml="$OUTPUT_PATH" + ;; + new) + pytest tests/test_binary_search_leftmost.py --junitxml="$OUTPUT_PATH" + ;; +esac