搜索旋转数组

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 二分查找

思路:该数组有两部分一定是有序的,对该数组进行判断,哪一部分是有序的,再判断目标值是否在该有序区间中,还是在无序区间中。

  1. 判断区间有序性
  2. 该目标值在有序区间
  3. 该目标值在无序区间

代码:

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 二分查找

思路:这里的二分需要多考虑几个问题,数字中有重复,中间值等于目标值了,可能左边还有,就不是返回值

  1. 左边等于目标值,可以直接返回
  2. 判断区间有序性,进一步判断目标值在哪一个区间
  3. 左区间有序,判断目标值是否在该有序区间
  4. 左区间无序,但右区间有序,判断目标值是否再该右区间有序区间
  5. 中间值和左边值相等,但不是目标值,则左边向后移动

代码:

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;
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值