1. 原题:
题目描述:
升序排列的整数数组 nums 在预先未知的某个点上进行了旋转(例如, [0,1,2,4,5,6,7] 经旋转后可能变为 [4,5,6,7,0,1,2] )。
请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
示例 3:
输入: nums = [1], target = 0
输出: -1
1.1 暴力查找
思路:每个数字都是独一无二的,则对数组进行一次遍历即可。
代码:
class Solution {
public:
int search(vector<int>& nums, int target) {
int sz = nums.size();
if(sz == 0)
return -1;
if(sz == 1)
return nums[0] == target ? 0 : -1;
for(int i = 0; i < sz; ++i)
{
if(nums[i] == target)//相等,返回
return i;
}
return -1;//数字不存在,返回-1
}
};
1.2 二分查找
思路:该数组有两部分一定是有序的,对该数组进行判断,哪一部分是有序的,再判断目标值是否在该有序区间中,还是在无序区间中。
- 判断区间有序性
- 该目标值在有序区间
- 该目标值在无序区间
代码:
class Solution {
public:
int search(vector<int>& nums, int target) {
int sz = nums.size();
if(sz == 0)
return -1;
if(sz == 1)
return nums[0] == target ? 0 : -1;
int start = 0, end = sz - 1;
while(start <= end)
{
int mid = (end + start) >> 1;
if(nums[mid] == target)//相等则返回
return mid;
if(nums[0] <= nums[mid])//左边是有序的
{
if(nums[0] <= target && target < nums[mid])//在左边
end = mid - 1;
else//在无序区间中
start = mid + 1;
}
else//无序区间
{
if(nums[mid] < target && target <= nums[end])//右边是有序的,目标值右边
start = mid + 1;
else//在左边
end = mid - 1;
}
}
return -1;
}
};
2. 有重复数字
题目描述:
搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。
链接:https://leetcode-cn.com/problems/search-rotate-array-lcci
示例1:
输入: arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 5
输出: 8(元素5在该数组中的索引)
示例2:
输入: arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 11
输出: -1 (没有找到)
2.1 暴力查找
思路:从前往后一次遍历即可,最先出现的就是返回的位置。
代码:
class Solution {
public:
int search(vector<int>& arr, int target) {
int sz = arr.size();
if(sz == 0)
return -1;
if(sz == 1)
return arr[0] == target ? 0 : -1;
for(int i = 0; i < sz; ++i)
{
if(arr[i] == target)
return i;
}
return -1;
}
};
2.2 二分查找
思路:这里的二分需要多考虑几个问题,数字中有重复,中间值等于目标值了,可能左边还有,就不是返回值
- 左边等于目标值,可以直接返回
- 判断区间有序性,进一步判断目标值在哪一个区间
- 左区间有序,判断目标值是否在该有序区间
- 左区间无序,但右区间有序,判断目标值是否再该右区间有序区间
- 中间值和左边值相等,但不是目标值,则左边向后移动
代码:
class Solution {
public:
int search(vector<int>& arr, int target) {
int sz = arr.size();
if(sz == 0)
return -1;
if(sz == 1)
return arr[0] == target ? 0 : -1;
int start = 0, end = sz - 1;
while(start <= end)
{
if(arr[start] == target)//左边相等了,则可以返回了
return start;
int mid = (start + end) >> 1;
if(arr[mid] == target)//中间相等,左边可能还有
end = mid;
else if(arr[mid] > arr[start])//左边有序
{
if(arr[mid] > target && arr[start] < target)//在有序区间
end = mid - 1;
else
start = mid + 1;
}
else if(arr[mid] < arr[start])//左边无序
{
if(arr[mid] < target && arr[end] >= target)//右边有序,在右边区间
start = mid + 1;
else
end = mid - 1;
}
else if(arr[mid] == arr[start])//start和mid相等,但不等于目标值,则start往前走
++start;
}
return -1;
}
};
2.3 寻找旋转位置,再两次二分查找
思路:找到旋转位置,进行先对左边进行二分查找,再对右边进行二分查找。左边若有,则直接返回。
代码:
class Solution {
public:
int search(vector<int>& arr, int target) {
int sz = arr.size();
if(sz == 0)
return -1;
if(sz == 1)
return arr[0] == target ? 0 : -1;
int start = 0, end = sz - 1;
int mid = 0;
for(int i = 0; i < sz; ++i)//寻找旋转位置
{
if(arr[i] > arr[i + 1])
{
mid = i;
break;
}
}
end = mid;
while(start < end)//查找左区间
{
int mid = (start + end) >> 1;
if(arr[mid] >= target)
end = mid;
else
start = mid + 1;
}
if(arr[start] == target)
return start;
start = mid + 1;
end = sz - 1;
while(start < end)//查找右区间
{
int mid = (start + end) >> 1;
if(arr[mid] >= target)
end = mid;
else
start = mid + 1;
}
if(arr[start] == target)
return start;
return -1;
}
};