搜索旋转排序数组(search-in-rotated-sorted-array)
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7]
可能变为 [4,5,6,7,0,1,2]
)。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
方法一:暴力法(Brute Force)
采用线性扫描的方式搜索
public class Solution {
public int search(int[] nums, int target) {
int len = nums.length;
for (int i = 0; i < len; i++) {
if (nums[i] == target) {
return i;
}
}
return -1;
}
}
方法二:二分查找(Binary Search)
不是完全连有序的数组,还能用二分查找的方法吗。可以!找到完全有序的部分,搜索target
即可。
代码一(leetcode官方)
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = (int)nums.size();
if (!n) return -1;
if (n == 1) return nums[0] == target ? 0 : -1;
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] == target) return mid;
if (nums[0] <= nums[mid]) {
if (nums[0] <= target && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[n - 1]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
};
试着解释一下nums[0] <= nums[mid]
。这句是用来判断mid左边为连续上升空间还是右边为连续上升空间。判断连接上升空间有什么用呢?如果这个数组是升序的,比如说[1,2,3,4,5,6,7,8,9]
,那这句话是没有意义的,因为前面的总是小于后面的,你还用个if
语句判断一下。这不是多此一举吗。
但问题是,题目中的数组是在某个点旋转过的。数组有可能变成了这样
[8,9,1,2,3,4,5,6,7]
或者这样
[3,4,5,6,7,8,9,1,2]
你应该看出什么来了,数组在某个位置旋转了一下,分成了两部分,第一个数组,第一部分,很短,那么中点就落在了第二部分,中点为3
。第二个数组,第一部分,很长,那么中点就落在了第一部分,中点为7
。
8到3不是完全连续上升的,完全有序区间在右边
3到7是完全连续上升的,完全有序区间在左边
因为二分搜索用来搜索有序的数组的,所以,我们通过这个条件来查找有充的部分,把target
放到其中搜索。
代码二
有序或部分有充基本使用二分搜索及其变种
将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。
此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环。
#include <stdio.h>
#include <vector>
class Solution {
public:
int search(std::vector<int>& nums, int target) {
int begin = 0;
int end = nums.size() - 1;
while(begin <= end){
int mid = (begin + end) / 2;
if (target == nums[mid]){
return mid;
}
else if (target < nums[mid]){
if (nums[begin] < nums[mid]){
if (target >= nums[begin]){
end = mid - 1;
}
else{
begin = mid + 1;
}
}
else if (nums[begin] > nums[mid]){
end = mid -1;
}
else if (nums[begin] == nums[mid]){
begin = mid + 1;
}
}
else if (target > nums[mid]){
if (nums[begin] < nums[mid]){
begin = mid + 1;
}
else if (nums[begin] > nums[mid]){
if (target >= nums[begin]){
end = mid - 1;
}
else{
begin = mid + 1;
}
}
else if (nums[begin] == nums[mid]){
begin = mid + 1;
}
}
}
return -1;
}
};
int main(){
int test[] = {9, 12, 15, 20, 1, 3, 6, 7};
std::vector<int> nums;
Solution solve;
for (int i = 0; i < 8; i++){
nums.push_back(test[i]);
}
for (int i = 0; i < 22; i++){
printf("%d : %d\n", i, solve.search(nums, i));
}
return 0;
}