Cracking the coding interview--Q9.3

Cracking the coding interview--Q9.3

January 18, 2013
作者:Hawstein
出处: http://hawstein.com/posts/9.3.html
声明:本文采用以下协议进行授权:  自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处。

题目

原文:

Given a sorted array of n integers that has been rotated an unknown number of times, give an O(log n) algorithm that finds an element in the array. You may assume that the array was originally sorted in increasing order.

EXAMPLE:

Input: find 5 in array (15 16 19 20 25 1 3 4 5 7 10 14)

Output: 8 (the index of 5 in the array)

译文:

一个数组有n个整数,它们排好序(假设为升序)但被旋转了未知次, 即每次把最右边的数放到最左边。给出一个O(log n)的算法找到特定的某个元素。

例子:

输入:在数组(15 16 19 20 25 1 3 4 5 7 10 14)中找出5

输出:8(5在数组中的下标)

解答

题目中提到了几个关键的词:有序,O(log n)。我们马上能联想到的就是二分查找算法。 但简单的二分查找肯定不行,因为这个数组被旋转了。也就是该数组前面一段有序, 后面一段有序,且前面那段的数要大于等于后面那段的数(本题考虑递增序列,递减同理)。 因此,我们需要对二分查找算法做一下修改,来解决这个具体的问题。

首先,我们来看一下函数原型:

int search(int a[], int low, int high, int x);

参数和二分查找一样,数组a,下界low,上界high及要查找的数x。当low <= high时, 我们会去求它们中间的那个数,然后与x对比,如果相同,就返回下标:

int mid = (low+high)>>1;
if(a[mid] == x) return mid;

如果a[mid]小于x,那么就要分情况讨论了。以上面给出的例子来说, 如果a[mid]小于a[low],说明a[mid]是在后半段上,假设现在a[mid]=3,x=5, (a[low]=15,a[high]=14),那么,接下来我们有两个选择, 可以在4 5 7 10 14这段找,也可以在15 16 19 20 25这段找。 所以,我们需要再做一次判断,如果a[low]<=x,说明x在15 16 19 20 25这段上, 我们更新high = mid – 1;如果a[low]>x,说明x在4 5 7 10 14这段上, 我们更新low = mid + 1;以上是a[mid]落在后半段的情况。当a[mid]>=a[low], a[mid]落在前半段,而x又比它大,则x只能是在15 16 19 20 25上, 所以只需更新low = mid + 1;

如果a[mid]大于x,分析类似。

代码如下:

#include <iostream>
using namespace std;

int search(int a[], int low, int high, int x){
    while(low <= high){
        //cout<<low<<" "<<high<<endl;
        //++cnt;
        int mid = (low+high)>>1;
        if(a[mid] == x) return mid;
        if(a[mid] < x){
            if(a[mid] < a[low]){
                if(a[low] <= x) high = mid - 1;
                else low = mid + 1;
            }
            else{
                low = mid + 1;
            }
        }
        else{
            if(a[mid] < a[low]){
                high = mid - 1;
            }
            else{
                if(a[low] <= x) high = mid - 1;
                else low = mid + 1;
            }
        }
    }
    return -1;
}
int main(){
    int a[12] = {
        15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14
    };
    int b[19] = {
        2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2
    };
    cout<<search(a, 0, 11, 3)<<endl;
    return 0;
}

对于有重复元素的数组,上面的算法失效。比如说代码中对于数组b, 它也是一个有序数组然后旋转了若干次。但是调用search函数将返回-1, 也就是查找失败。

Cracking the coding interview—问题与解答

全书的C++代码托管在Github上:

https://github.com/Hawstein/cracking-the-coding-interview

你可能感兴趣的:(Cracking the coding interview--Q9.3)