题目来源LeetCode: LeetCode OJ


题目描述: Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.

题目解释

一个排好序的数组,不知道以哪个点为中心旋转了(部分有序),你的任务是查找给定的数是否存在数组中。在的话返回下标,不在的话返回-1.

分析

查找首先想到O(log(n))的二分查找,但是二分查找的前提是有序数组。题目里一个有序数组旋转后变成了部分有序。通过比较两端大小找到增序部分。

eg: 4 5 6 0 1 2 3
first = 4, mid = 0, last = 3,
通过比较first,mid,last找到增序部分。
在这个例子中为1 2 3,
然后判断target是否在这个增序子序列中,
如果在则直接用二分查找,
不在则在另一部分(例子中为4 5 6 0)中继续分解。

Code

int search(const vector<int>& nums, int target)
{
    int first = 0;
    int last = nums.size();
    while(first != last)
    {
        const int mid = (first+last)>>1;    // 使用位运算加速
        if(nums[mid] == target)
            return mid;
        if(nums[first] <= nums[mid])    // 找到增序子序列
            if(nums[first]<=target && target<nums[mid])    // 找到target在哪个部分
                last = mid;
            else
                first = mid+1;
        else
            if(nums[mid]<target && target<=nums[last-1])
                first = mid+1;
            else
                last = mid;
    }
    return -1;
}

拓展

当数组为无序时的二分查找

分析

一种是先排序再二分,一种是结合快排思想,每次选择一个关键字,比他大的放右边,比他小的放左边,然后再比较他和需要查找的数的关系,再选择区间进行迭代。如果需要返回查找数的下标,则添加一个纪录下标的数组,这样排好序后也能知道当前数在原始数组中的位置。

初始数组3 1 2 5 4 7 0 6
mid = key = 3 进行一次快排填坑
得到数组0 1 2 3 4 7 5 6
比较mid与target
如果target>mid则迭代mid后半部分
如果target<mid则迭代mid前半部分
直到找到target

Code

int BinarySearch(vector<int>& nums, int target)
{
    int num = nums.size();
    int index[num];        // index数组纪录下标 以便能找到在数组的初始位置
    for(int i = 0; i < num; i++)    // 初始化index数组
        index[i] = i;
    int l, r, m, sl, sr, mIndex;
    l = 0, r = num-1;
    while(l<=r)        // 开始迭代
    {
        mIndex = index[l], m = nums[l];
        sl = l, sr = r;
        while(sl<sr)    // 快排思想,左右填坑,并用index记录位置
        {
            while(sl<sr && m<nums[sr])
                sr--;
            nums[sl] = nums[sr];
            index[sl] = index[sr];
            while(sl<sr && m>nums[sl])
                sl++;
            nums[sr] = nums[sl];
            index[sr] = index[sl];
        }
        nums[sl] = m;
        index[sl] = mIndex;
        if(m == target)
            return mIndex;
        if(target > m)        // 判断target在哪个区间
            l = sl+1;
        else
            r = sl-1;
    }
    return -1;
}

相关题目

Search in Rotated Sorted Array II

相关题目分析

因为允许出现重复数字,但是数组还是部分有序的,所以跳过重复数字即可

Code

int search(const vector<int>& nums, int target)
{
    int first = 0;
    int last = nums.size();
    while(first != last)
    {
            const int mid = (first+last)>>1;
        if(nums[mid] == target)
            return mid;
        if(nums[first] < nums[mid])
            if(nums[first]<=target && target<nums[mid])
                last = mid;
            else
                first = mid+1;
        else if(nums[first] > nums[mid])
        {
            if(nums[mid]<target && target<=nums[last-1])
                first = mid+1;
            else
                last = mid;
        }
        else    // 特判相等时跳过
            first++;
    }
    return -1;
}