今天面试遇到这道题了。当场很勉强做出来,但是事后想想好多细节还是没有想清楚。
希望本次记录彻底解决本题。
首先,这道题如果没有重复元素,且只是判断是否能找到 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; } };
来源:https://www.cnblogs.com/hezhiqiangTS/p/12423396.html