[LeetCode] 81. Search in Rotated Sorted Array II
今天面试遇到这道题了。当场很勉强做出来,但是事后想想好多细节还是没有想清楚。
希望本次记录彻底解决本题。
首先,这道题如果没有重复元素,且只是判断是否能找到 target,那么很简单:
while(start + 1 < end){
mid = start + (end - start) / 2;
if(nums[mid] == target)
return true;
if(nums[mid] > nums[start]){ // mid 处于前一线段
if(nums[start] <= target && target < nums[mid])// 注意一定是 nums[start] <= target
end = mid;
else
start = mid;
}
else{
// nums[mid] < nums[end]
if(nums[mid] < target && target <= nums[end])// 注意这一一定是 target <= nums[end]
start = mid;
else
end = mid;
}
}
唯一需要注意的就是 nums[start] 和 nums[end] 有可能直接等于 target,一定要考虑这种情况,否则会移错区间。比如当 nums[start] == target,但是代码为
if(nums[start] < target && target < nums[mid])
end = mid;
else
start = mid;
会错误地将执行 start = mid
现在处理出现重复元素的情况。最直观的想法是将本情况手动转换成之前不重复的情况。事实上这也是个人觉得最好的最简洁的做法。
如何转换呢,由于输入数组是从有序序列旋转得到的,那么旋转之后重复元素只可能出现在:
- 全部为重复元素
- 出现在头或者尾或者同时出现在尾和头。
那么说明我们只需要判断头尾即可,所以我们可以在循环前部处理,将前后重复的部分消除。
while(nums[start] == nums[start + 1] && start + 1 < end)
start++;
while(nums[end] == nums[end - 1] && start + 1 < end)
end--;
这样处理之后,唯一可能还剩下的重复情况就是 nums[start] == nums[end],数组中其他所有元素都是不相等的。这时这两个相等的元素其实对我们最后的判断已经没有影响了,因为在前面我们已经考虑过
nums[start] == target 以及 nums[end] == target 的情况了,而我们的目的只是判断 nums 中是否有 target,所以不论最后找到的位置是 start 还是 end 都可以。
完整代码:
class Solution {
public:
bool search(vector<int>& nums, int target) {
if(nums.size() <= 0)
return false;
int start = 0, end = (int)nums.size() - 1;
while(start + 1 < end){
// 处理头尾重复元素
while(nums[start] == nums[start + 1] && start + 1 < end){
start++;
}
while(nums[end] == nums[end - 1] && start + 1 < end){
end--;
}
// 按照无重复元素处理
int mid = start + (end - start) / 2;
if(nums[mid] == target)
return true;
if(nums[mid] > nums[start]){
if(nums[start] <= target && target < nums[mid]){
end = mid;
}
else{
start = mid;
}
}
else if(nums[mid] < nums[end]){
if(nums[end] >= target && target > nums[mid]){
start = mid;
}
else{
end = mid;
}
}
}
if(nums[start] == target || nums[end] == target)
return true;
return false;
}
};
如果我们不用这种方法,那么我们需要更加深入地去判断每种可能出现地情况。
当 nums[mid] > nums[start] 和 nums[mid] < nums[end] 都不满足时,则: numd[start] <= nums[mid] 并且 nums[mid] >= nums[end]
如果我们再仔细想,由于数组旋转之前整体有序,那么旋转之后得到的两条线段本身应该是有序的,而且,nums[start] 一定会大于等于 nums[end],那么
- 当 nums[start] < nums[mid] 时,nums[mid] 只可能等于 nums[end],而不可能大于 nums[end];
- 当 nums[mid] > nums[end] 时,nums[start] 只可能等于 nums[mid],而不可能小于nums[mid]
这样其实我们就很好做了:
// nums[mid] <= nums[start] && nums[mid] >= nums[end]
else if(nums[start] > nums[mid]){ // nums[mid] == nums[end]
end--;
} else if(nums[mid] > nums[end]) { // nums[start] == nums[mid]
start++;
} else{ // start = mid = end
start++;
end--;
}
完整代码如下:
class Solution {
public:
bool search(vector<int>& nums, int target) {
if (nums.size() <= 0) return false;
int start = 0, end = (int)nums.size() - 1;
while (start + 1 < end) {
int mid = start + (end - start) / 2;
if (nums[mid] == target) return true;
if (nums[mid] > nums[start]) {
if (nums[start] <= target && target < nums[mid]) {
end = mid;
} else {
start = mid;
}
} else if (nums[mid] < nums[end]) {
if (nums[end] >= target && target > nums[mid]) {
start = mid;
} else {
end = mid;
}
}
// mid <= start, mid >= end
else if(nums[start] > nums[mid]){
// nums[mid] == nums[end]
end--;
} else if(nums[mid] > nums[end]) {
// nums[start] == nums[mid]
start++;
} else{
// start = mid = end
start++;
end--;
}
}
if (nums[start] == target || nums[end] == target) return true;
return false;
}
};