感觉这个题目在哪里看到过,面试有可能会被问起,所以还是记录一下。
题目地址:Search in Rotated Sorted Array
题目大意:有一组有序数据,从中间任意一个点进行一次旋转,如:0 1 2 4 5 6 7
可能变成 4 5 6 7 0 1 2
现在要在这组数里面寻找一个值,如果存在,返回他的下标值,如果不存在,返回-1;
解题思路:
O(N)方法:最简单的方法,遍历数据里面的每个数,判断与给定数是否相等。这种方法没有利用题目给的数据的有序特性。
O(logN)方法:采用二分查找。
二分查找,刚开始的时候,编写的二分查找,基本把所有的情况都给列出来了,首先根据nums[left]和nums[right]的大小,区分数组是全部有序的,还是具有拐点的,如果全部有序,则进行普通的二分查找。如果具有拐点,则把所有的情况都列出来,在进行相应的二分,更新left和right的值。
代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
int length=nums.size();
int left=0;
int right=length-1;
if(nums[left]>target && nums[right]<target)
return -1;
int mid;
while(left<=right && left>=0 && right<length)
{
mid=(left+right)/2;
if(nums[mid]==target)
return mid;
if(nums[left]==target)
return left;
if(nums[right]==target)
return right;
if(nums[left]>nums[right]) //有逆转
{
if(nums[mid]>nums[left])
{
if(target>=nums[left] && target<nums[mid])
right=mid-1;
else left=mid+1;
}
else {
if(target>nums[mid] && target<=nums[left])
left=mid+1;
else right=mid-1;
}
}
else{
if(nums[mid]>target)
right=mid-1;
else left=mid+1;
}
}
return -1;
}
};
方法缺点:感觉思路太不清晰了,在列出全部的情况的过程中,很容易把一些情况漏掉,而且代码可读性太差,换个人可能都不知道要干什么。如果再让我写一次,很大可能写不对。
改进:改进想法,先把那个转折点找出来,再根据target的大小来进行二分查找。这样,二分查找就是普通的二分查找了。
代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
int minPoint;
if(nums[left]<=nums[right])
return binarySearch(nums,target,left,right);
if(nums[left]>nums[right])
minPoint=searchRotatedPoint(nums);
if(target>=nums[left])
return binarySearch(nums,target,left,minPoint-1);
if(target<nums[left])
return binarySearch(nums,target,minPoint,right);
}
int searchRotatedPoint(vector<int> nums)
{ int left=0;
int right=nums.size()-1;
int mid;
while(left<right)
{
mid=(left+right)/2;
if(nums[mid]>nums[left])
left=mid;
if(nums[mid]<nums[left])
right=mid;
if(right-left==1)
return nums[left]>nums[right]?right:left;
}
return left;
}
int binarySearch(vector<int> &nums, int target, int start, int end)
{
int left=start;
int right=end;
int mid;
while(left<right)
{
mid=(left+right)/2;
if(nums[mid]==target)
return mid;
if(nums[mid]>target)
right=mid-1;
if(nums[mid]<target)
left=mid+1;
}
if(left==right && nums[left]==target)
return left;
return -1;
}
};
这个方法的话思路比较清晰,代码可读性也较好。比较容易复现