📑 题目:34. 在排序数组中查找元素的第一个和最后一个位置

🚀 本题 LeetCode 传送门

题目大意

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。你的算法时间复杂度必须是 O(log n) 级别。如果数组中不存在目标值,返回 [-1, -1]。

解题思路

  • 给出一个有序数组 nums 和一个数 target,要求在数组中找到第一个和这个元素相等的元素下标,最后一个和这个元素相等的元素下标。

  • 这一题是经典的二分搜索变种题。二分搜索有 4 大基础变种题:

    1. 查找第一个值等于给定值的元素
    2. 查找最后一个值等于给定值的元素
    3. 查找第一个大于等于给定值的元素
    4. 查找最后一个小于等于给定值的元素

    这一题的解题思路可以分别利用变种 1 和变种 2 的解法就可以做出此题。或者用一次变种 1 的方法,然后循环往后找到最后一个与给定值相等的元素。不过后者这种方法可能会使时间复杂度下降到 O(n),因为有可能数组中 n 个元素都和给定元素相同。(4 大基础变种的实现见代码)

代码

  1. package leetcode
  2. func searchRange(nums []int, target int) []int {
  3. return []int{searchFirstEqualElement(nums, target), searchLastEqualElement(nums, target)}
  4. }
  5. // 二分查找第一个与 target 相等的元素,时间复杂度 O(logn)
  6. func searchFirstEqualElement(nums []int, target int) int {
  7. low, high := 0, len(nums)-1
  8. for low <= high {
  9. mid := low + ((high - low) >> 1)
  10. if nums[mid] > target {
  11. high = mid - 1
  12. } else if nums[mid] < target {
  13. low = mid + 1
  14. } else {
  15. if (mid == 0) || (nums[mid-1] != target) { // 找到第一个与 target 相等的元素
  16. return mid
  17. }
  18. high = mid - 1
  19. }
  20. }
  21. return -1
  22. }
  23. // 二分查找最后一个与 target 相等的元素,时间复杂度 O(logn)
  24. func searchLastEqualElement(nums []int, target int) int {
  25. low, high := 0, len(nums)-1
  26. for low <= high {
  27. mid := low + ((high - low) >> 1)
  28. if nums[mid] > target {
  29. high = mid - 1
  30. } else if nums[mid] < target {
  31. low = mid + 1
  32. } else {
  33. if (mid == len(nums)-1) || (nums[mid+1] != target) { // 找到最后一个与 target 相等的元素
  34. return mid
  35. }
  36. low = mid + 1
  37. }
  38. }
  39. return -1
  40. }
  41. // 二分查找第一个大于等于 target 的元素,时间复杂度 O(logn)
  42. func searchFirstGreaterElement(nums []int, target int) int {
  43. low, high := 0, len(nums)-1
  44. for low <= high {
  45. mid := low + ((high - low) >> 1)
  46. if nums[mid] >= target {
  47. if (mid == 0) || (nums[mid-1] < target) { // 找到第一个大于等于 target 的元素
  48. return mid
  49. }
  50. high = mid - 1
  51. } else {
  52. low = mid + 1
  53. }
  54. }
  55. return -1
  56. }
  57. // 二分查找最后一个小于等于 target 的元素,时间复杂度 O(logn)
  58. func searchLastLessElement(nums []int, target int) int {
  59. low, high := 0, len(nums)-1
  60. for low <= high {
  61. mid := low + ((high - low) >> 1)
  62. if nums[mid] <= target {
  63. if (mid == len(nums)-1) || (nums[mid+1] > target) { // 找到最后一个小于等于 target 的元素
  64. return mid
  65. }
  66. low = mid + 1
  67. } else {
  68. high = mid - 1
  69. }
  70. }
  71. return -1
  72. }