Reference: LeetCode
Difficulty: Medium

Post: 4 Solutions Including Divide-and-Conquer, Binary Search (+ Follow-Up)

Problem

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

Find the minimum element.

You may assume no duplicate exists in the array.

Example:

1
2
3
4
5
Input: [3,4,5,1,2] 
Output: 1

Input: [4,5,6,7,0,1,2]
Output: 0

Analysis

Sorting

1
2
3
4
5
6
public:
int findMin(vector<int>& nums)
{
sort(nums.begin(), nums.end());
return nums[0];
}

Time: $O(N\log{N})$
Space: $O(1)$

Brute-Force

1
2
3
4
5
6
7
8
9
10
11
12
13
public:
int findMin(vector<int>& nums)
{
int min = nums[0];
for (auto& val : nums)
{
if (val < min)
{
min = val;
}
}
return min;
}

Time: $O(N)$
Space: $O(1)$

Divide & Conquer

Reference: Huahua

In the mid3 case, the result could be calculated in $O(1)$.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public:
int findMin(vector<int>& nums)
{
return findMin(nums, 0, nums.size() - 1);
}

private:
int findMin(const vector<int>& nums, int lo, int hi)
{
// only one element
if (lo >= hi)
return nums[lo];

if (isSorted(nums, lo, hi))
return nums[lo];

int mid = lo + (hi - lo) / 2;
int left = findMin(nums, lo, mid);
int right = findMin(nums, mid + 1, hi);
return min(left, right);
}

bool isSorted(const vector<int>& nums, int lo, int hi)
{
return nums[lo] < nums[hi];
}

Time: $O(\log{N})$

  • False: $T(N) = 2T(N/2) = O(N)$ ❌
    • At each level, at least one side could be done in $O(1)$.
  • True: $T(N) = O(1) + T(N/2) = O(\log{N})$

Space: $O(\log{N})$

The basic idea is that if nums[mid] >= nums[lo] we assure that the inflection point is on the right part.

Recursion:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public:
int findMin(vector<int>& nums)
{
return findMin(nums, 0, nums.size() - 1);
}

private:
int findMin(const vector<int>& nums, int lo, int hi)
{
// only one element
if (lo == hi)
return nums[lo];

// the subarray is sorted
if (nums[lo] < nums[hi])
return nums[lo];

int mid = lo + (hi - lo) / 2;

if (nums[mid] >= nums[lo])
{
return findMin(nums, mid + 1, hi);
}
else
{
return findMin(nums, lo, mid);
}
}

Iteration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public:
int findMin(vector<int>& nums)
{
int lo = 0;
int hi = nums.size() - 1;
// stops when there is only one element
// in other words, it makes sure that there are at least 2 elements
while (lo < hi)
{
if (nums[lo] < nums[hi]) return nums[lo];

int mid = lo + (hi - lo) / 2;
if (nums[mid] >= nums[lo])
{
lo = mid + 1;
}
else
{
hi = mid;
}
}
return nums[lo];
}

Time: $O(\log{N})$
Space: $O(\log{N})$ or $O(1)$