返回

LeetCode第34题:在排序数组中找到元素的第一个和最后一个位置

后端

揭秘LeetCode第34题:在排序数组中查找元素的第一个和最后一个位置

简介

欢迎来到算法探索之旅!LeetCode第34题,中等难度,是一个经典的数据结构与算法问题,旨在考察你的逻辑思维能力和编程技巧。在这篇博客中,我们将深入探讨这道题目的最佳解法,分析其复杂度,并提供全面的代码示例。

题目剖析

给定一个按照升序排列的数组nums和一个目标值target,我们的任务是找到target在数组中出现的第一个和最后一个位置。如果target不存在于数组中,则返回[-1,-1]

解题思路

针对这道题,主要有两种常用的解法:

二分查找

二分查找是一种高效的算法,利用数组有序的特性,通过不断缩小搜索范围来查找元素。具体步骤如下:

  1. 初始化两个指针leftright,分别指向数组的第一个和最后一个元素。
  2. 计算数组的中点mid,并与target进行比较。
  3. 如果nums[mid]小于target,则target在数组的右半部分,将left指针移动到mid的下一位。
  4. 如果nums[mid]大于target,则target在数组的左半部分,将right指针移动到mid的前一位。
  5. 重复步骤2-4,直到找到targetleftright指针交叉。

遍历查找

遍历查找是一种更直观的解法,它从数组的第一个元素开始,逐个元素与target进行比较,直到找到target或遍历完整个数组。

代码实现

Python

def search_range(nums, target):
    """
    :type nums: List[int]
    :type target: int
    :rtype: List[int]
    """
    # 二分查找
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid + 1
        elif nums[mid] > target:
            right = mid - 1
        else:
            # 找到目标值,然后向左和向右扩展,找到第一个和最后一个位置
            start, end = mid, mid
            while start >= 0 and nums[start] == target:
                start -= 1
            while end < len(nums) and nums[end] == target:
                end += 1
            return [start + 1, end - 1]

    # 目标值不存在于数组中
    return [-1, -1]

Java

public int[] searchRange(int[] nums, int target) {
    int[] result = new int[]{-1, -1};

    int left = 0;
    int right = nums.length - 1;

    // 二分查找目标值
    while (left <= right) {
        int mid = left + (right - left) / 2;

        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            // 找到目标值,向左和向右扩展
            int start = mid;
            int end = mid;

            while (start >= 0 && nums[start] == target) {
                start--;
            }
            while (end < nums.length && nums[end] == target) {
                end++;
            }

            result[0] = start + 1;
            result[1] = end - 1;
            break;
        }
    }

    return result;
}

复杂度分析

二分查找

  • 平均时间复杂度: O(log n),其中n是数组的长度。
  • 最坏时间复杂度: O(n),当数组中不存在目标值时。

遍历查找

  • 时间复杂度: O(n),因为需要遍历整个数组。

注意: 二分查找的平均时间复杂度远低于遍历查找,但在最坏情况下,它们的时间复杂度相同。

总结

LeetCode第34题是一个经典的算法问题,考察了我们的逻辑思维能力和编程技巧。通过二分查找或遍历查找,我们可以有效地找到目标值在排序数组中的第一个和最后一个位置。二分查找在平均情况下效率更高,但遍历查找更容易理解和实现。选择哪种方法取决于具体情况和性能需求。

常见问题解答

  1. 为什么二分查找的平均时间复杂度是O(log n)?
    因为每一步二分查找将搜索范围缩小一半,导致搜索空间呈指数级下降。

  2. 二分查找为什么在最坏情况下需要O(n)的时间复杂度?
    当目标值不存在于数组中时,二分查找需要遍历整个数组才能得出结论。

  3. 遍历查找比二分查找简单,为什么不总是使用遍历查找?
    当数组规模较大时,二分查找的平均时间复杂度远低于遍历查找。

  4. 我可以同时使用二分查找和遍历查找吗?
    可以,如果优先考虑性能,可以使用二分查找来快速找到目标值的大致位置,然后再使用遍历查找来精确找到第一个和最后一个位置。

  5. LeetCode第34题还有其他解法吗?
    除了二分查找和遍历查找之外,还可以使用哈希表来存储元素的位置,实现常数时间查找。但是,这需要额外的空间复杂度。