33. 搜索旋转排序数组

题目描述

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [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

算法

说实话,看到评论中有人在讨论这题的意义何在,接着给出了遍历寻找的O(n)复杂度的3行代码,下面还有6、7个人点赞,我是十分无语的。

题目既然给出算法时间复杂度必须是 O(log n) 级别,且原数组是有序变形而来,那么能想到的第一解决方案是二分查找

二分查找

前提:数组从小到大升序

"""left\right分别为数组nums的首末指针, target是要寻找的数"""
while (left <= right):
    mid = (left + right) / 2
    if (nums[mid] == target):
        return mid
    if (nums[mid] > target):
        right = mid - 1
    else
        left = mid + 1
# 如果跳出循环还没有找到这个数,说明数组中没有target,返回-1
return -1;

目前的数组nums并不是从小到大排好序的,而是间断性的有序,如4,5,6,7,0,1,2。这里仍可以借鉴二分查找的思想,只不过对mid要增加些许判断条件

原数组有序的情况下是0,1,2,3,4,5,6,7,旋转过后分为几种情况,即小 大 小、大 小 大的两种形式,对应位置为letf\mid\right这3个指针。小到大还是正常从小到大的顺序,在这一区域中仍可以用正常的二分思想确定left和right。在另一边大到小的乱序中,这需要根据nums[mid]和边界值的判断来确定left和right。详细注释给在代码中

代码

class Solution {
public:
    int search(vector<int>& nums, int target) {
        /*** 利用二分查找求解问题 ***/
        
        // 三个指针
        int left, right, mid;
        
        // 赋初值
        left = 0, right = nums.size() - 1;

        // 循环开始
        while (left <= right)
        {
            // 计算中间位置的下标
            mid = (left + right) / 2;

            // 分别判断3个指针是否有目标值
            if (nums[mid] == target)
               return mid;
            if (nums[left] == target)
                return left;
            if (nums[right] == target)
                return right;
            
            // 如果当前判断的这段数组满足nums[left] < nums[right],那么说明这段数组的顺序是从小到大排序的,形如0,1,2,3,4中,0 < 4是成立的
            if (nums[left] < nums[right])
            {
                // 对于正常从小到大排序的数组二分查找
                if(nums[mid] > target)
                    right = mid - 1;
                else
                    left = mid + 1;
            }
            // 如果这段数组满足nums[left] > nums[right],那么说明这段数组的顺序并不是完全从小到大排序的,形如3,4,0,1,2中,3 > 2是成立的
            else
            {
                if (nums[mid] < nums[right])
                {
                    // 大 小 大,小到大这一段是正常从小到大排序的,如果target处于这一段,意味着下一次的判断需要移到这一段来寻找
                    if (target > nums[mid] && target < nums[right])
                        left = mid + 1;
                    else
                    // 否则,往左边段寻找
                        right = mid - 1;
                }
                else
                {
                    // 小 大 小,同理,小到大是正常排序的,如果target处于这一段,意味着下一次的判断需要移到这一段来寻找
                    if (target < nums[mid] && target > nums[left])
                        right = mid - 1;
                    else
                     // 否则,往右边段寻找
                        left = mid + 1;
                }
            }
        }
        // 前面都没能返回直至跳出循环说明要寻找的数并不在数组中,返回-1
        return -1;
    }
};
posted @ 2019-02-11 17:13  小王点点  阅读(267)  评论(0编辑  收藏  举报