题目链接
既然输入的数组是排序的,那么我们就能很自然地想到用二分查找算法。在题目给的例子中,我们可以先用二分查找找到一个3,由于3可能出现多次,因此我们找到的3的左右两边可能都有3,于是在找到的3的左右两边顺序扫描,分别找出第一个3和最后一个3。因为要查找的数字在长度为n的数组中有可能出现O(N)次,所以顺序扫描的时间复杂度是O(N),这种算法的效率与直接从头到尾顺序扫描整个数组统计3出现的次数方法是一样的,显然我们可以有更加高效的算法。
我们思考如何更好地利用二分法查找算法,假设我们要统计数字k在排序数组中出现的次数。在前面的算法中,时间主要消耗在如何确定重复出现的数字的第一个k和最后一个k的位置上,有没有可能用二分查找直接找到第一个和最后一个k呢。
我们先复习如何用二分查找在数组中找到第一个k。二分查找算法总是先拿数组中中间的数字和k做比较,如果中间的数字比k大,那么k只有可能出现在数组的前半段,下一轮我们只在数组的前半段查找就可以了。反之,我们在数组的后半段查找。如果中间的数字和k相等呢?我们先判断这个数字是不是第一个k,如果中间数字的前一个数字不是k,那么此时中间的数字刚好就是第一个k;如果中间数字的前面一个数字也是k那么说明第一个k肯定在数组的前半段,下一轮我们仍需要在数组的前半段查找。基于这种思路我们可以写出如下代码。这个算法的时间复杂度是O(logN),空间复杂度是O(1)。当然,第一种方法仍然有点臃肿,我们可以合并两个二分查找的过程,并且转换思路为查找插入target和target-1的位置(若和数组中的数字相同则插入到右边),这样可以分别找到要找的数字序列的起点和终点,代码实现如3所示。