package com.freetymekiyan.algorithms.level.hard;
import org.junit.Assert;
import org.junit.Test;
/**
* Suppose a sorted array is rotated at some pivot unknown to you beforehand.
* <p>
* (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
* <p>
* You are given a target value to search. If found in the array return its index, otherwise return -1.
* <p>
* You may assume no duplicate exists in the array.
* <p>
* Company Tags: LinkedIn, Bloomberg, Uber, Facebook, Microsoft
* Tags: Binary Search, Array
* Similar Problems: (M) Search in Rotated Sorted Array II, (M) Find Minimum in Rotated Sorted Array
*/
public class SearchInRotatedSortedArr {
/**
* Binary Search.
* Find minimum value's index first.
* Then compare target with the ending value to know which half to search.
* If target <= nums[length - 1], start from min index, end at last.
* If target > nums[length - 1], start from 0, end at min index.
* Then do a regular binary search.
*/
public int search(int[] nums, int target) {
int minIdx = findMinIdx(nums);
if (target == nums[minIdx]) {
return minIdx;
}
int len = nums.length;
int start = target <= nums[len - 1] ? minIdx : 0;
int end = target <= nums[len - 1] ? len - 1 : minIdx - 1;
while (start <= end) {
int mid = start + (end - start) / 2;
if (nums[mid] == target) {
return mid;
} else if (target > nums[mid]) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return -1;
}
/**
* Binary Search.
* Find the minimum value's index.
* Compare the number in the middle with the number at the end.
* If nums[mid] > nums[end], minimum in right half, excluding mid, search in [mid + 1, end].
* If nums[mid] = nums[end], no dup, means mid = end, minimum is mid.
* If nums[mid] < nums[end], mid to end is increasing, minimum in left half, search in [start, mid], end = mid.
* If the number in the middle > the end, right half.
* If the number in the middle < the end, can be middle or left half.
*/
private int findMinIdx(int[] nums) {
int start = 0, end = nums.length - 1;
while (start < end) { // Stop when start == end.
int mid = start + (end - start) / 2;
if (nums[mid] > nums[end]) { // Minimum must be in right half, excluding mid.
start = mid + 1;
} else { // Minimum must be in left half, including mid.
end = mid;
}
}
return start;
}
/**
* Binary Search.
* If nums[mid] == target, return mid.
* If nums[lo] <= nums[mid], mid is in rotated left half.
* | If target < nums[mid] and target >= nums[lo], target is in the same half.
* | hi = mid - 1
* | else target is not in the same half.
* | lo = mid + 1
* If nums[mid] <= nums[hi], mid is in rotated right half.
* | If target > nums[mid] and target <= nums[hi], target is in the same half.
* | lo = mid + 1
* | else target is not in the same half.
* | hi = mid - 1
*/
public int searchB(int[] nums, int target) {
int lo = 0;
int hi = nums.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (nums[mid] == target) {
return mid;
}
if (nums[lo] <= nums[mid]) {
if (target < nums[mid] && target >= nums[lo]) {
hi = mid - 1;
} else {
lo = mid + 1;
}
}
if (nums[mid] <= nums[hi]) {
if (target > nums[mid] && target <= nums[hi]) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
}
return -1;
}
@Test
public void testExamples() {
int[] nums = {5, 1, 3};
Assert.assertEquals(0, search(nums, 5));
}
}