1. 程式人生 > >求遞增數列中所有A[i]=i的元素(阿里巴巴2013筆試題)

求遞增數列中所有A[i]=i的元素(阿里巴巴2013筆試題)

求遞增數列中所有A[i]=i的元素

閱讀更多部落格,請訪問博主的個人網站 點選訪問網站

題意描述

給定一個排好升序的陣列A[1]、A[2]、……、A[n],其元素兩兩不相等。請設計一高效的演算法找出中間所有A[i] = i的下標。並分析其複雜度。

思路分析

該題目有兩個重要的條件:
1. 排好升序的陣列
2. 元素兩兩不相等

我們根據以上兩個已知條件可以得出以下定理:

定理1:在遞增序列中A中,對於A的所有元素A[i],如果滿足A[i] = i, 則他們組成一個連續的序列。

對於定理1這裡不作證明,只給出兩個例子來闡述定理的意義。
1. 已知A中滿足條件的元素只有A[i],A[j],A[k]三個,且i < j < k,那麼我們根據定理1就可以斷言:i + 1 = j 且 j + 1 = k。
2. 已知A中有元素A[i] = i, A[i + 1] = i + 1,但是A[i + 2] ≠ i + 2,那麼A[i + 2]後面的元素也不會再有滿足條件的元素了。同理,若A[i - 1] ≠i - 1,那麼可以推出A[i - 1]前面也不會再有滿足條件的元素了。

演算法設計

由定理1知,所有滿足條件的元素挨在一塊組成一個連續數列。我們的演算法就是找出這個數列的左邊界和右邊界。
1. 二分查詢左邊界,即滿足 A[i] = i 且 A[i - 1] < i - 1的位置。
2. 二分查詢右邊界,即滿足A[i] = i 且A[i + 1] > i + 1。
3. 左邊界到右邊界之間的即為滿足條件的元素。

程式碼實現(python)

class Solution:
    def getLeftBound(self, a):
        low = 0
        high = len(a) - 1

        while
low <= high: mid = (low + high) / 2 if a[mid] == mid and (mid == 0 or a[mid - 1] != mid - 1): return mid elif a[mid] < mid: low = mid + 1 elif a[mid] > mid: high = mid - 1 else: high = mid - 1
return -1 def getRightBound(self, a): low = 0 high = len(a) - 1 while low <= high: mid = (low + high) / 2 if a[mid] == mid and (mid == len(a) - 1 or a[mid + 1] != mid + 1): return mid elif a[mid] < mid: low = mid + 1 elif a[mid] > mid: high = mid - 1 else: low = mid + 1 return -1 def getAns(self, a): l = self.getLeftBound(a) r = self.getRightBound(a) return a[l : r + 1] s = Solution() a = [-1, 0, 2, 3, 4, 5, 6, 9] print s.getAns(a)

複雜度分析(時間)

  • 程式碼中getLeftBound()和getRightBound()都是二分查詢的過程,複雜度為O(logn)
  • 最後返回滿足條件的元素 的操作,複雜度為O(n)