求递增数列中所有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)

你可能感兴趣的:(算法)