我们知道在一段有序的数组里面查找某一个数字时可以使用二分查找算法来实现o(lgn)时间复杂度的查找,现在题目给我们的数组是有序数组的旋转。比如原数组为(1,2,3,4,5,6,7),旋转后可能变成(2,3,4,5,6,7,1)或者(4,5,6,7,1,2,3)等等。可以发现旋转后的数组不在满足有序的特性了,因此不能直接的套用二分查找算法,但是观察可以发现,旋转之后的数组是分段有序的,比如:(4,5,6,7,1,2,3)数组中前半段(4,5,6,7)与后半段(1,2,3)都是递增序列,可以分别在两段数组上进行二分查找操作。因此在每次循环中需要判断哪一段是递增的。
LeetCode-33. Search in Rotated Sorted Array
输入:旋转数组,目标值
输出:返回目标值在旋转数组中的下标,如果不存在就返回 -1。
注意:旋转数组中的数值都不相同。
AC代码:
class Solution {
public:
int search(vector<int>& nums, int target) {
if(nums.empty())
return -1;
int lo = 0;
int hi = (int)nums.size() - 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;
}
else //后一段递增
{
if(target > nums[mid] && target <= nums[hi])
lo = mid + 1;
else
hi = mid - 1;
}
}
return -1;
}
};
LeetCode--81. Search in Rotated Sorted Array II
接着上一题:
输入:旋转数组,目标值。
输出:如果目标值出现在旋转数组中,输出true,否则输出false。
注意:本题与上一题的区别主要是旋转数组中可以有重复的数字。
在上一题中我们根据nums[lo]、nums[mid]、nums[hi]之间的关系来判断递增段,但是如果旋转数组中存在重复的数字的话,还需要多判断一种情况。比如考虑旋转数组:{1,1,0,1,1,1,1},第一次循环时,lo = 0,hi = 6,mid = 3。此时nums[lo] = nums[mid] = nums[hi] = 1,这样我们就没有办法判断出lo --- mid与mid --- hi 段哪一段是递增的了,这时可以只能采用线性遍历lo-hi段得出结论了。
AC代码如下:
class Solution {
public:
bool search(vector<int>& nums, int target) {
if(nums.empty()) {
return false;
}
int lo = 0;
int hi = nums.size() - 1;
while(lo <= hi) {
int mid = lo + (hi - lo) / 2;
if(nums[mid] == nums[lo] && nums[mid] == nums[hi]) {
return count(nums.begin() + lo, nums.begin() + hi + 1, target) != 0;//STL函数
}
else if(nums[mid] == target) {
return true;
} else if(nums[lo] <= nums[mid]) { //前一段递增
if(target < nums[mid] && target >= nums[lo]) {
hi = mid - 1;
} else {
lo = mid + 1;
}
} else { //后一段递增
if(target <= nums[hi] && target > nums[mid]) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
}
return false;
}
};
LeetCode--153. Find Minimum in Rotated Sorted Array
输入:旋转数组;
输出:旋转数组的最小值;
注意:旋转数组中没有最小值。
这题与第一题类似,如果前一段是递增序列,那么最小值在后面;如果后一段是递增序列,那么最小值在前面。
AC代码:
class Solution {
public:
int findMin(vector<int>& nums) {
int lo = 0;
int hi = (int)nums.size() - 1;
while(lo < hi) {
int mid = lo + (hi - lo) / 2;
if(nums[mid] > nums[lo] && nums[mid] > nums[hi]) { //前一段是递增序列
lo = mid + 1;
} else if(nums[mid] < nums[hi] && nums[mid] < nums[lo]){
//后一段是递增序列,注意到,这个地方 hi 不能等于 mid - 1,因为mid可能指向的就是最小值的索引。
hi = mid;
} else { //执行到这里说明旋转数组是递增或者递减的,比如:没有旋转或者旋转的长度为数组的长度减1,那么返回首尾两端的最小值即可。
return min(nums[lo], nums[hi]);
}
}
return nums[lo];
}
};
LeetCode--154. Find Minimum in Rotated Sorted Array II
接着上一题,输入输出都是一样的,不同的是本题中旋转数组中可以有重复的元素。那么与第二题类似,需要考虑一种特殊情况,就是无法判断哪一段是递增的时候,这时采用线性比较,找出最小值。
AC代码:
class Solution {
public:
int findMin(vector<int>& nums) {
int lo = 0;
int hi = (int)nums.size() - 1;
while(lo < hi) {
int mid = lo + (hi - lo) / 2;
if(nums[mid] == nums[lo] && nums[mid] == nums[hi]) { //无法判断哪一段递增,递减
return *min_element(nums.begin() + lo, nums.begin() + hi + 1);//STL函数
} else if(nums[mid] >= nums[lo] && nums[mid] > nums[hi]) { //前一段递增
lo = mid + 1;
} else if(nums[mid] <= nums[hi] && nums[mid] < nums[lo]) { //后一段递增
hi = mid ;
} else { //执行到这里说明旋转数组是递增或者递减的,比如:没有旋转或者旋转的长度为数组的长度减1,那么返回首尾两端的最小值即可。
return min(nums[lo],nums[hi]);
}
}
return nums[lo];
}
};