Search for a Range

Question

  1. Given a sorted array of integers, find the starting and ending position of a given target value.
  2. Your algorithm's runtime complexity must be in the order of O(log n).
  3. If the target is not found in the array, return [-1, -1].
  4. Example
  5. Given [5, 7, 7, 8, 8, 10] and target value 8,
  6. return [3, 4].

題解

Search for a range 的題目可以拆解為找 first & last position 的題目,即要做兩次二分。由上題二分查找可找到滿足條件的左邊界,因此只需要再將右邊界找出即可。注意到在(target == nums[mid]時賦值語句為end = mid,將其改為start = mid即可找到右邊界,解畢。

Java

  1. /**
  2. * 本代碼fork自九章算法。沒有版權歡迎轉發。
  3. * http://www.jiuzhang.com/solutions/search-for-a-range/
  4. */
  5. public class Solution {
  6. /**
  7. *@param A : an integer sorted array
  8. *@param target : an integer to be inserted
  9. *return : a list of length 2, [index1, index2]
  10. */
  11. public ArrayList<Integer> searchRange(ArrayList<Integer> A, int target) {
  12. ArrayList<Integer> result = new ArrayList<Integer>();
  13. int start, end, mid;
  14. result.add(-1);
  15. result.add(-1);
  16. if (A == null || A.size() == 0) {
  17. return result;
  18. }
  19. // search for left bound
  20. start = 0;
  21. end = A.size() - 1;
  22. while (start + 1 < end) {
  23. mid = start + (end - start) / 2;
  24. if (A.get(mid) == target) {
  25. end = mid; // set end = mid to find the minimum mid
  26. } else if (A.get(mid) > target) {
  27. end = mid;
  28. } else {
  29. start = mid;
  30. }
  31. }
  32. if (A.get(start) == target) {
  33. result.set(0, start);
  34. } else if (A.get(end) == target) {
  35. result.set(0, end);
  36. } else {
  37. return result;
  38. }
  39. // search for right bound
  40. start = 0;
  41. end = A.size() - 1;
  42. while (start + 1 < end) {
  43. mid = start + (end - start) / 2;
  44. if (A.get(mid) == target) {
  45. start = mid; // set start = mid to find the maximum mid
  46. } else if (A.get(mid) > target) {
  47. end = mid;
  48. } else {
  49. start = mid;
  50. }
  51. }
  52. if (A.get(end) == target) {
  53. result.set(1, end);
  54. } else if (A.get(start) == target) {
  55. result.set(1, start);
  56. } else {
  57. return result;
  58. }
  59. return result;
  60. // write your code here
  61. }
  62. }

源碼分析

  1. 首先對輸入做異常處理,數組為空或者長度為0
  2. 初始化 start, end, mid三個變量,注意mid的求值方法,可以防止兩個整型值相加時溢出
  3. 使用迭代而不是遞歸進行二分查找
  4. while終止條件應為start + 1 < end而不是start <= endstart == end時可能出現死循環
  5. 先求左邊界,迭代終止時先判斷A.get(start) == target,再判斷A.get(end) == target,因為迭代終止時target必取start或end中的一個,而end又大於start,取左邊界即為start.
  6. 再求右邊界,迭代終止時先判斷A.get(end) == target,再判斷A.get(start) == target
  7. 兩次二分查找除了終止條件不同,中間邏輯也不同,即當A.get(mid) == target如果是左邊界(first postion),中間邏輯是end = mid;若是右邊界(last position),中間邏輯是start = mid
  8. 兩次二分查找中間勿忘記重置 start, end 的變量值。

C++

  1. class Solution {
  2. /**
  3. *@param A : an integer sorted array
  4. *@param target : an integer to be inserted
  5. *return : a list of length 2, [index1, index2]
  6. */
  7. public:
  8. vector<int> searchRange(vector<int> &A, int target) {
  9. // good, fail are the result
  10. // When found, returns good, otherwise returns fail
  11. int N = A.size();
  12. vector<int> fail = {-1, -1};
  13. if(N == 0)
  14. return fail;
  15. vector<int> good;
  16. // search for starting position
  17. int lo = 0, hi = N;
  18. while(lo < hi){
  19. int m = lo + (hi- lo)/2;
  20. if(A[m] < target)
  21. lo = m + 1;
  22. else
  23. hi = m;
  24. }
  25. if(A[lo] != target)
  26. return fail;
  27. good.push_back(lo);
  28. // search for ending position
  29. lo = 0; hi = N;
  30. while(lo < hi){
  31. int m = lo + (hi - lo)/2;
  32. if(target < A[m])
  33. hi = m;
  34. else
  35. lo = m + 1;
  36. }
  37. good.push_back(lo - 1);
  38. return good;
  39. }
  40. };

源碼分析

與前面題目類似,此題是將兩個子題組合起來,前半為找出”不小於target的最左元素”,後半是”不大於target的最右元素”,同樣的,使用開閉區間[lo, hi)仍然可以簡潔的處理各種邊界條件,僅須注意在解第二個子題”不大於target的最右元素”時,由於每次lo更新時都至少加1,最後會落在我們要求的位置的下一個,因此記得減1回來,若直覺難以理解,可以使用一個例子在紙上推一次每個步驟就可以體會。