Link to the problem

The problem is here: https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/

Problem description

The problem is very straightforward. Given a sorted array (ascending) nums and a number target, find the starting and the ending position of target.

Idea

This is a very typical problem that can be solved by using binary search.

Binary search

The boundary conditions are essentially important for a bug-free binary search algorithm. My method is to figure out the loop invariant and then strictly follow that invariant. There could be some corner cases that violate the loop invariant at the very beginning. We should handle those cases carefully.

Implementation

STL

The c++ standard library actually provides [std::lower_bound](https://www.cplusplus.com/reference/algorithm/lower_bound/) and [std::upper_bound](https://www.cplusplus.com/reference/algorithm/upper_bound/), which can be used to solve this problem directly.

lower_bound is to find the first element >= target (not less than) upper_bound is to find the first element > target (strictly greater)

DIY

This is my implementation:

  1. class Solution {
  2. // return the position of first element >= target; -1 if no such element
  3. int lower_bound(const vector<int> &nums, const int target) {
  4. // handle special corner cases
  5. if (nums.empty() || nums.back() < target) {
  6. return -1;
  7. }
  8. if (nums[0] >= target) {
  9. return 0;
  10. }
  11. int i = 0, j = nums.size() - 1;
  12. // loop inv: nums[i] < target and nums[j] >= target
  13. while (i < j - 1) {
  14. if (nums[i] >= target || nums[j] < target) {
  15. return -1;
  16. }
  17. int mid = (i + j) / 2;
  18. if (nums[mid] < target) {
  19. i = mid;
  20. } else {
  21. j = mid;
  22. }
  23. }
  24. return j >= 0 && nums[j] >= target ? j : -1;
  25. }
  26. // return the position of the first element > target; -1 if no such element
  27. int upper_bound(const vector<int> &nums, const int target) {
  28. // handle special corner cases
  29. if (nums.empty() || nums.back() <= target) {
  30. return -1;
  31. }
  32. if (nums[0] > target) {
  33. return 0;
  34. }
  35. int i = 0, j = nums.size() - 1;
  36. // loop inv: nums[i] <= target and nums[j] > target
  37. while (i < j - 1) {
  38. if (nums[i] > target || nums[j] <= target) {
  39. return -1;
  40. }
  41. int mid = (i + j) / 2;
  42. if (nums[mid] <= target) {
  43. i = mid;
  44. } else {
  45. j = mid;
  46. }
  47. }
  48. return j >= 0 && nums[j] > target ? j : -1;
  49. }
  50. public:
  51. vector<int> searchRange(vector<int>& nums, int target) {
  52. int n = nums.size();
  53. int i = 0, j = n - 1;
  54. vector<int> res(2, -1);
  55. auto lb = this->lower_bound(nums, target);
  56. auto ub = this->upper_bound(nums, target);
  57. if (lb == -1 || nums[lb] != target) {
  58. // can not find target
  59. } else {
  60. res[0] = lb;
  61. res[1] = ub == -1 ? n - 1 : ub - 1;
  62. }
  63. return res;
  64. }
  65. };

Possible refinement:

  • Can use template-programming to reuse code.