Given a rotated sorted array arr
, return the minimum value in the array.
Every element in the array is a unique integer.
Example:
Explanation: The element 1 is the minimum value in the array.
We want to take advantage of the fact that the Array is sorted, and do a Binary Search.
1. Recursion
The first thing to realize is the array has 2 parts. We'll call them "left" and "right":
It's hard to know where to start, so let's consider some random example to try and make progress. Say we pick this example:
In this case, our search should continue towards the left side of the array since that's where the smallest value "1" is. We can do this by searching between i
and mid
. *
In general, whenever we find ourselves in the "right" part of the array, we want to continue searching towards the left.
The other case is if mid
is in the "left" part of the array.
In this case, we should search towards the right:
Here's the recursion, putting these two cases together:
How do we figure out if mid is in the "left" or "right" part of the array?
If mid is in the right part, then we'll have arr[mid] <= arr[j]
:
If mid is in the left part, then we'll have arr[mid] > arr[j]
:
Here's the recursion, filling this in:
It's important to confirm to yourself that these conditions work even in small cases like searching 3, 2, or 1 element, and searching in an unrotated array.
2. Base case
The recursion should keep going until the search interval has narrowed on one element.
3. Code
Here's the full code, combining the recursion and the base case:
Time Complexity O(logn)
Space Complexity O(logn) for the Call Stack.
You can make the above code iterative, like this:
Time Complexity O(logn)
Space Complexity O(1)
Here's an alternative solution that uses hijacking. It's slightly more efficient because it uses j = mid-1
, eliminating elements faster. But it's a little harder to work through all the edge cases.
Here's how you write this code with i = mid+1
and j = mid-1
.
1. Recursion
As this recursion runs, we want to keep track of the minimum element that we've seen. The easiest way to do this is to add a global variable that keeps track of this. You need to realize that the minimum element is the leftmost element in the right part of the array.
The binary search always gets closer to this value, so whenever you're in the right part of the array, you can just update the global variable, and at the end it will be the correct value.
To check if mid is in the right or left part of the array, we can't just use arr[mid] <= arr[j]
like we did before. Our new recursion here might eliminate the minimum element from the search bounds, and if that happens, this condition will give True regardless of whether we're in the left or right part of the array.
To fix this, you can use the comparison arr[mid] <= arr[-1]
instead. This works because it compares the middle element with the global very last element, so even if you eliminate the last element from your search bounds it's still valid. This condition is pretty difficult to come up with, considering something like arr[0] <= arr[mid]
doesn't work due to the fact that mid gets rounded down. To be confident in your answer, you need to make sure it works on edge cases
like searches of size 3, 2, and 1, and even unrotated searches.
2. Base case
When the search has eliminated all the elements, it should stop.
3. Code
We can code this up by combining the recursion and the base case. Here's the recursive code:
Time Complexity O(logn)
Space Complexity O(logn) for the Call Stack.
Here's the iterative version:
TreeNode.of
function can be used to create an entire binary tree using 1 line of code. The input is a BFS traversal of the tree including nulls, and the output is the root node of the tree.TreeNode.of([1, None, 2, None, 3])
ListNode.of
function lets you create an entire Linked List using 1 line of code. The input is an array, and the output is the head of the Linked List.ListNode.of([1, 2, 3])
ListNode.of([1, 2, 3], 1)
GraphNode.of
function lets you create an entire Graph using 1 line of code. The input is an Adjacency Matrix, and the output is the first node in the matrix.GraphNode.of([ ['A', 1, 2], ['B', 2], ['C', 0] ])
[['__init__', 15], ['add', 16], ['get']]
c = MyClass(15) c.add(16) c.get()
TreeNode.of
function can be used to create an entire binary tree using 1 line of code. The input is a BFS traversal of the tree including nulls, and the output is the root node of the tree.TreeNode.of([1, None, 2, None, 3])
ListNode.of
function lets you create an entire Linked List using 1 line of code. The input is an array, and the output is the head of the Linked List.ListNode.of([1, 2, 3])
ListNode.of([1, 2, 3], 1)
GraphNode.of
function lets you create an entire Graph using 1 line of code. The input is an Adjacency Matrix, and the output is the first node in the matrix.GraphNode.of([ ['A', 1, 2], ['B', 2], ['C', 0] ])
[['__init__', 15], ['add', 16], ['get']]
c = MyClass(15) c.add(16) c.get()