Skip to content

Commit

Permalink
feat(puzzles): cyclically shifted array
Browse files Browse the repository at this point in the history
Using binary search to find the index of the smallest number in a cyclically shifted
array
  • Loading branch information
BrianLusina authored and BrianLusina committed Nov 22, 2024
1 parent acafb89 commit 55a1b7c
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 0 deletions.
22 changes: 22 additions & 0 deletions puzzles/search/binary_search/cyclically_shifted_array/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Cyclically Shifted Array

Return the index of the smallest number in a cyclically shifted array.

An array is “cyclically shifted” if it is possible to shift its entries cyclically so that it becomes sorted.

The following list is an example of a cyclically shifted array:

A = [4, 5, 6, 7, 1, 2, 3]

Below are all the possible cyclic shifts of an array:

```plain
Array A: [1,2,3,4,5,6,7]
[2,3,4,5,6,7,1]
[5,6,7,1,2,3,4]
[3,4,5,6,7,1,2]
[6,7,1,2,3,4,5]
[4,5,6,7,1,2,3]
[7,1,2,3,4,5,6]
```
29 changes: 29 additions & 0 deletions puzzles/search/binary_search/cyclically_shifted_array/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from typing import List

def find_index_of_smallest_number(numbers: List[int]) -> int:
"""
Finds the index of the smallest number in a cyclically shifted array
Args:
numbers (list): cyclically shifted array
Returns:
int: index of the smallest number in a cyclically shifted array
"""
left_pointer = 0
right_pointer = len(numbers) - 1

while left_pointer < right_pointer:
middle = (left_pointer + right_pointer) >> 1

# if the number in the middle is less than or equal to the number on the right, this means that the search space
# should be moved to the left. Eliminating numbers on the right by moving the right pointer to the middle. This
# is because the numbers are increasing, therefore the numbers we are looking for can not be on the right
if numbers[middle] <= numbers[right_pointer]:
right_pointer = middle
elif numbers[middle] > numbers[right_pointer]:
# move the left pointer to the middle + 1 to eliminate the search space on the left if the middle number
# is greater than the number at the right pointer. This is because the numbers are decreasing from the middle
# therefore we can possibly find the number we are searching for in the right search space
left_pointer = middle + 1

return left_pointer
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import unittest
from . import find_index_of_smallest_number


class FindIndexOfSmallestNumberTestCase(unittest.TestCase):
def test_1(self):
"""should return 4 in input of [4, 5, 6, 7, 1, 2, 3]"""
numbers = [4, 5, 6, 7, 1, 2, 3]
expected = 4
actual = find_index_of_smallest_number(numbers)
self.assertEqual(expected, actual)

def test_2(self):
"""should return 2 in input of [6, 7, 1, 2, 3, 4, 5]"""
numbers = [6, 7, 1, 2, 3, 4, 5]
expected = 2
actual = find_index_of_smallest_number(numbers)
self.assertEqual(expected, actual)

def test_3(self):
"""should return 1 in input of [7, 1, 2, 3, 4, 5, 6]"""
numbers = [7, 1, 2, 3, 4, 5, 6]
expected = 1
actual = find_index_of_smallest_number(numbers)
self.assertEqual(expected, actual)

def test_4(self):
"""should return 0 in input of [1, 2, 3, 4, 5, 6, 7]"""
numbers = [1, 2, 3, 4, 5, 6, 7]
expected = 0
actual = find_index_of_smallest_number(numbers)
self.assertEqual(expected, actual)

def test_5(self):
"""should return 5 in input of [3, 4, 5, 6, 7, 1, 2]"""
numbers = [3, 4, 5, 6, 7, 1, 2]
expected = 5
actual = find_index_of_smallest_number(numbers)
self.assertEqual(expected, actual)

def test_6(self):
"""should return 6 in input of [2, 3, 4, 5, 6, 7, 1]"""
numbers = [2, 3, 4, 5, 6, 7, 1]
expected = 6
actual = find_index_of_smallest_number(numbers)
self.assertEqual(expected, actual)

def test_7(self):
"""should return 3 in input of [5, 6, 7, 1, 2, 3, 5]"""
numbers = [5, 6, 7, 1, 2, 3, 5]
expected = 3
actual = find_index_of_smallest_number(numbers)
self.assertEqual(expected, actual)

if __name__ == '__main__':
unittest.main()
3 changes: 3 additions & 0 deletions puzzles/search/binary_search/integer_square_root/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ def integer_square_root(k: int) -> int:
mid_squared = mid * mid

if mid_squared <= k:
# this discards all the numbers less than mid, i.e. moves the left pointer to be 1 greater than mid
left = mid + 1
else:
# otherwise, we discard all the numbers greater than mid, i.e. move the right pointer to 1 less than mid
right = mid - 1

# this becomes the largest integer whose square is less than the provided integer k
return left - 1

0 comments on commit 55a1b7c

Please sign in to comment.