[CareerCup] 9.3 Magic Index 魔法序號

Grandyang發表於2015-09-18

 

9.3 A magic index in an array A[0.. .n-1] is defined to be an index such that A[i] = i. Given a sorted array of distinct integers, write a method to find a magic index, if one exists, in array A.
FOLLOW UP
What if the values are not distinct?

 

這道題定義了一個魔法序號,就是一個陣列的序號等於該位置的值的時候,這個序號就是魔法序號,給了我們一個有序陣列,讓我們來找魔法序號。這裡brute force的方法就不提了,因為沒啥考察的目的,對於高效的查詢方法我們就要首先考慮二分搜尋法,首先我們來看這種方法,沒啥特別的地方,套用一般的二分查詢法的格式即可,參見程式碼如下:

 

class Solution {
public:
    int getMagicIdx(vector<int> &nums) {
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == mid) return mid;
            else if (nums[mid] > mid) right = mid - 1;
            else left = mid + 1;
        }
        return -1;
    }
};

 

這道題的Follow up是說如果陣列由重複項怎麼處理,那麼傳統的二分搜尋法就會失效,因為下列這種情況可能存在:

-10 -5 2 2 2 3 4 7 9 12 13
0 1 2 3 4 5 6 7 8 9 10

這種情況符合題意,但是左右兩邊都會出現魔法序號,所以二分查詢法會失效。那麼我們難道又要用地毯式搜尋了麼,其實也不必,我們可以用一種類似於二分搜尋法的遞迴方法來解決問題,就拿上面那個例子來說,第一次找到比較完中間點後,由於左右兩邊都會出現答案,所以我們左右半段要分別遞迴一下,這裡我們可以加一個trick來優化演算法,比如要遞迴左半段時,那麼新的右邊界就可以設為min(mid - 1, nums[mid]),同理遞迴右半段時,左邊界可以設為max(mid + 1, nums[mid])。還有個小trick,就是如果左半段搜到了答案,那麼直接返回即可,不用再搜右半段,因為題目讓我們找一個就行了,沒說要找出所有的Magic index,參見程式碼如下:

 

// Follow up
class Solution {
public:
    int getMagicIdx(vector<int> &nums) {
        return getMagicIdxDFS(nums, 0, nums.size() - 1);
    }
    int getMagicIdxDFS(vector<int> &nums, int start, int end) {
        if (end < start) return -1;
        int mid = (start + end) / 2;
        if (mid == nums[mid]) return mid;
        int left = getMagicIdxDFS(nums, start, min(mid - 1, nums[mid]));
        if (left >= 0) return left;
        int right = getMagicIdxDFS(nums, max(mid + 1, nums[mid]), end);
        return right;
    }
};

 

相關文章