题意:Suppose a sorted array 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.
题解:因为原数组已排好序,则翻转后的各个子数组仍然有序,且子数组之间仍然有序,且能发现翻转后最小的子数组位于新数组的最右边。
则我们使用二分每次只和最右边的数比较。
- 如果num[mid] > num[nSize-1],则说明他们属于不同数组,我们应该把left赋值mid+1,使之逐渐靠近最后一个有序子数组。
- 如果num[mid] < num[nSize-1],则说明他们属于同一数组,我们应该把right赋值mid,使之逐渐靠近最后一个有序子数组的最左边,即最小值。
代码如下:
class Solution {
public:
int findMin(vector<int>& nums) {
int nSize = nums.size();
if(nSize == 0) {
return 0;
}
int left = 0;
int right = nSize-1;
while(left < right) {
int mid = ((left+right)>>1);
if(nums[mid] > nums[nSize-1]) {
left = mid+1;
}
else {
right = mid;
}
}
return nums[left];
}
};
以上若允许数字相同,前提条件仍然不变,但是在遇到与最右值相同的情况下,则有两种情况:
- 若nums[mid]与最右值都在同一个子数组,则right应该赋值mid。
- 若nums[mid]与最右值不在同一个子数组,则left = mid+1逐渐靠近最小的子数组。
代码如下:
class Solution {
public:
int findMinPart(vector<int>& nums, int left, int right, int edgeVal) {
if(left == right) {
return nums[left];
}
while(left < right) {
int mid = (left+right)>>1;
if(nums[mid] > edgeVal) {
left = mid+1;
}
else if(nums[mid] < edgeVal) {
right = mid;
}
else {
return min(findMinPart(nums, left, mid, edgeVal), findMinPart(nums, mid+1, right, edgeVal));
}
}
return nums[left];
}
int findMin(vector<int>& nums) {
return findMinPart(nums, 0, nums.size()-1, nums[nums.size()-1]);
}
};
同理可得Search in Rotated Sorted Array
代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
if(nums.size() == 0) return -1;
int left = 0;
int right = nums.size()-1;
int mid;
while(left <= right) {
mid = (left+right)>>1;
if(nums[mid] == target) return mid;
if(nums[left] <= nums[mid]) {
//left到mid为升序
if(nums[mid] > target && nums[left] <= target) {
//target在有序数组中
right = mid-1;
}
else {
left = mid+1;
}
}
else {
//left和mid在不同有序数组中
if(nums[left] <= target || nums[mid] > target) {
//target在left和mid之间
right = mid-1;
}
else {
left = mid+1;
}
}
}
return -1;
}
};
在有重复的旋转数组中找target
class Solution {
public:
bool search(vector<int>& nums, int target) {
if (nums.empty()) {
return false;
}
int left = 0;
int right = nums.size() - 1;
while(left < right - 1) {
int mid = left + (right - left) / 2;
if (nums[mid] == target || nums[left] == target || nums[right] == target) {
return true;
}
if (nums[left] == nums[mid] && nums[mid] == nums[right]) {
left++;
right--;
} else if (nums[mid] >= nums[left]) {
// left, mid || right
// left, mid, right ||
// 这两种情况下什么时候进入[left, mid]空间?也就是target处于left和mid之间才会唯一的进入此空间
if (nums[left] < target && nums[mid] > target) {
right = mid - 1;
} else {
// 此处讨论target在[left, mid]空间之外的情况
// left, mid | right,如果nums[mid] < target,则类似left, mid, right ||
// target在mid || 之间;如果nums[left] > target,可知target在 || right之间
left = mid + 1;
}
} else {
// left || mid, right
// 此种情况,a[l] >= a[r] >= a[m],又因为三者不可能相等,所以a[l] > a[m]
// 所以通过nums[left] > nums[mid]可判断是此种情况
if (nums[mid] < target && nums[right] > target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return nums[left] == target || nums[right] == target;
}
};