搜索旋转排序数组(search-in-rotated-sorted-array)

搜索旋转排序数组(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;
}

资料

lee官方含视频
liweiwei1419

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值