Given a sorted array of integers, find the starting and ending position of a given target value. Your algorithm's runtime complexity must be in the order of O(log n). If the target is not found in the array, return [-1, -1]. For example, Given [5, 7, 7, 8, 8, 10] and target value 8, return [3, 4].
Analysis: 这道题是二分查找Search Insert Position的变体,思路并不复杂,就是先用二分查找找到其中一个target,然后再往左右找到target的边缘。找边缘的方法跟二分查找仍然是一样的,只是相等的情况依然要往左找(找左边缘)或往右找(找右边缘)。这样下来总共进行了三次二分查找,所以算法的时间复杂度仍是O(logn),空间复杂度是O(1)。
Notice: 对停止的边界条件极不熟悉,需要总结,参见Binary Search的Summary
本题的精髓和难点在于寻找左右边沿,方法是Binary Search的变体,只不过这次是要左右相遇。以找左边缘为例,while循环里要么A[m] < target, l 跳到m+1, 要么A[m]==target, r 跳到m-1, 直到l > r为止,这时候l所在的位置就是要求的左边缘。而如果是找右边缘,l > r之后,r所在位置就是要求的右边缘。l和r所停的位置正好是目标数的后面和前面。
1 public class Solution { 2 public int[] searchRange(int[] A, int target) { 3 int start = 0, end = A.length - 1; 4 int[] result = new int[2]; 5 result[0] = -1; 6 result[1] = -1; 7 if(A==null || A.length==0) 8 { 9 return result; 10 } 11 int middle = search(A, start, end, target, result); 12 if (middle == -1) return result; 13 result[0] = searchleft(A, 0, middle, target); 14 result[1] = searchright(A, middle, A.length-1, target); 15 return result; 16 } 17 18 public int search(int[] A, int start, int end, int target, int[] result) { 19 int mid = -1; 20 if (start <= end) { 21 mid = (start + end) / 2; 22 if (A[mid] == target) { 23 result[0] = mid; 24 result[1] = mid; 25 return mid; 26 } 27 else if (A[mid] < target) { 28 start = mid + 1; 29 return search(A, start, end, target, result); 30 } 31 else { 32 end = mid - 1; 33 return search(A, start, end, target, result); 34 } 35 } 36 else { 37 result[0] = -1; 38 result[1] = -1; 39 return mid; 40 } 41 42 } 43 44 public int searchleft(int[] A, int start, int end, int target) { 45 if (start > end) return start; 46 int mid = (start + end) / 2; 47 if (A[mid] < target) { 48 start = mid + 1; 49 return searchleft(A, start, end, target); 50 } 51 else { 52 end = mid - 1; 53 return searchleft(A, start, end, target); 54 } 55 } 56 57 public int searchright(int[] A, int start, int end, int target) { 58 if (start > end) return end; 59 int mid = (start + end) / 2; 60 if (A[mid] <= target) { 61 start = mid + 1; 62 return searchright(A, start, end, target); 63 } 64 else { 65 end = mid - 1; 66 return searchright(A, start, end, target); 67 } 68 } 69 }
上述两个子函数searchLeft和searchRight其实都可以不用递归,用迭代来写:(这是本题的精髓和难点所在), 跟Search Insert Position这个问题的方法一样,这道题可以认为是在找一个比target略小的目标数(因为等于target的时候移动的是右边缘),这个目标数在A中不存在,所以当循环结束时候,l, r分别处在目标数的后面和前面。如果我们找的是左边缘,那么这个左边缘应该是在目标数右边,所以就是l所处的位置
1 public int findLeft(int[] A, int target, int right) { 2 int l = 0; 3 int r = right; 4 while (l <= r) { 5 int m = (l + r) / 2; 6 if (A[m] == target) { 7 r = m - 1; 8 } 9 else { 10 l = m + 1; 11 } 12 } 13 return l; 14 } 15 16 public int findRight(int[] A, int target, int left) { 17 int l = left; 18 int r = A.length - 1; 19 while (l <= r) { 20 int m = (l + r) / 2; 21 if (A[m] == target) { 22 l = m + 1; 23 } 24 else { 25 r = m - 1; 26 } 27 } 28 return r; 29 }
整体如果想用iterative的方法来写:
1 public class Solution { 2 public int[] searchRange(int[] A, int target) { 3 int[] res = new int[2]; 4 res[0] = -1; 5 res[1] = -1; 6 if(A==null || A.length==0) 7 { 8 return res; 9 } 10 int l=0; 11 int r=A.length-1; 12 int m=(l+r)/2; 13 while(l<=r) 14 { 15 m=(l+r)/2; 16 if(A[m]==target) 17 { 18 res[0]=m; 19 res[1]=m; 20 break; 21 } 22 else if(A[m]>target) 23 { 24 r = m-1; 25 } 26 else 27 { 28 l = m+1; 29 } 30 } 31 if(A[m]!=target) 32 return res; 33 int newL = m; 34 int newR = A.length-1; 35 while(newL<=newR) 36 { 37 int newM=(newL+newR)/2; 38 if(A[newM]==target) 39 { 40 newL = newM+1; 41 } 42 else 43 { 44 newR = newM-1; 45 } 46 } 47 res[1]=newR; 48 newL = 0; 49 newR = m; 50 while(newL<=newR) 51 { 52 int newM=(newL+newR)/2; 53 if(A[newM]==target) 54 { 55 newR = newM-1; 56 } 57 else 58 { 59 newL = newM+1; 60 } 61 } 62 res[0]=newL; 63 64 return res; 65 66 } 67 }