Reference: LeetCode
Difficulty: Hard

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 duplicate exists in the array.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Input: [3,4,5,1,2] 
Output: 1

Input: [2,2,2,0,1]
Output: 0

Input: [3,3,3,3]
Output: 3

Input: [3,6,3]
Output: 3

Input: [3,1,3]
Output: 1

Follow-up Questions:

  • Would allow duplicates affect the run-time complexity? How and why?

Analysis

Sorting & Brute-Force as in 153.

Divide & Conquer

Reference: Huahua

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

Note: Duplicates affect the run-time complexity. Consider the following case:

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]; // must be < (<= will fail in the case [3, 1, 3])
}

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.

However, unlike the solution in 153, we need 3 cases:

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
29
30
31
32
33
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;

// changes here --> 3 cases
if (nums[mid] > nums[lo])
{
return findMin(nums, mid + 1, hi);
}
else if (nums[mid] < nums[lo])
{
return findMin(nums, lo, mid);
}
else // nums[mid] == nums[lo]
{
return min(findMin(nums, mid + 1, hi), findMin(nums, lo, mid));
}
}

Iteration:

Invalid!

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
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 if (nums[mid] > nums[lo])
{
hi = mid;
}
else
{
// ???
}
}
return nums[lo];
}

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

  • In the worst case scenario it would become $O(N)$.

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