力扣刷题入门,你有被第一题难到吗?

296 阅读8分钟

image.png

前言

在刚看到这个题目的时候大家是不是觉得非常简单呢?毕竟题目上都有“简单”的标签,但当大家看了之后就会发现这道题并没有想象中那么简单,不然社区里就不会有“有人相爱,有人夜里开车看海,有人leetcode第一题都做不出来。”这句名言了,哈哈哈。 但其实只要我们有一定的算法基础就不难看出来,在解决这道题之前,首先需要明确题目的要求和约束条件。题目要求给定一个整数数组 nums 和一个整数目标值 target,需要在数组中找出和为目标值 target 的两个整数,并返回它们的数组下标。同时,题目规定每种输入只会对应一个答案,但数组中同一个元素在答案里不能重复出现。针对这个问题,我们可以想到多种解决方法。一种常见的解法是利用哈希表,另一种是使用双指针。接下来,我们将分别介绍这两种解法的思路和实现过程,并比较它们的优缺点。

哈希表解决方法

我们可以利用哈希表来存储已经遍历过的数字以及它们的索引,以便在后续的遍历中快速查找到与当前数字配对的另一个数字。

让我们一步步来解释这个解法:

  1. 我们首先创建一个空的哈希表 map,用来存储数组中的元素和它们的索引。在 JavaScript 中,可以使用 Map 数据结构来实现哈希表。
const map = new Map();
  1. 接下来,我们进行一次遍历数组 nums,在每次遍历中,计算当前元素与目标值 target 的差值 complement。我们需要找到另一个数字与当前数字的和为目标值 target
for (let i = 0; i < nums.length; i++) {
    const complement = target - nums[i];
}
  1. 对于当前遍历的数字 nums[i],我们检查哈希表中是否存在键为 complement 的项,即是否存在另一个数字与当前数字的和为目标值 target
if (map.has(complement)) {
    // 找到目标数字对应的另一个数字,返回它们的索引
    return [map.get(complement), i];
}
  1. 如果哈希表中不存在 complement 这个键,则将当前数字及其索引存入哈希表中。
map.set(nums[i], i);
  1. 如果遍历完成后仍然没有找到符合条件的数字对,则返回一个空数组,表示未找到满足条件的数字对。
return [];

完整解题代码:

function twoSum(nums, target) {
    const map = new Map();

    for (let i = 0; i < nums.length; i++) {
        const complement = target - nums[i];
        if (map.has(complement)) {
            return [map.get(complement), i];
        }
        map.set(nums[i], i);
    }

    return [];
}

image.png 通过这种方法,我们只需要一次遍历数组,就可以在平均情况下在常量时间内完成查找操作,因此时间复杂度为 O(n),其中 n 是数组 nums 的长度。这个解法避免了暴力搜索的高时间复杂度,使得解题更加高效。

双指针解决方法

function twoSum(nums, target) {
    // 克隆并排序数组
    const sortedNums = [...nums].sort((a, b) => a - b);

    // 初始化双指针
    let left = 0;
    let right = sortedNums.length - 1;

    // 双指针遍历
    while (left < right) {
        const sum = sortedNums[left] + sortedNums[right];
        if (sum === target) {
            // 找到目标数字对应的两个数字
            // 在原数组中查找这两个数字的下标
            const index1 = nums.indexOf(sortedNums[left]);
            let index2 = nums.indexOf(sortedNums[right]);
            // 如果两个数字相同,则从 index2 后面查找第二个数字的下标
            if (sortedNums[left] === sortedNums[right]) {
                index2 = nums.indexOf(sortedNums[right], index1 + 1);
            }
            return [index1, index2];
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }

    // 遍历完成,未找到符合条件的数字对
    return [];
}

image.png

  1. **排序数组:**首先,我们将给定的整数数组 nums 进行排序。这是因为双指针方法通常要求数组是有序的,这样才能更方便地进行双指针遍历。
  2. **初始化双指针:**我们初始化两个指针 leftright,分别指向数组的头部和尾部。在排序后的数组中,left 指针指向的元素比 right 指针指向的元素小。
  3. **双指针遍历:**在每次遍历过程中,我们计算指针所指向的两个数的和,并与目标值进行比较。如果和等于目标值,则说明我们找到了符合条件的两个数,它们的下标分别为 leftright。如果和小于目标值,则将 left 指针向右移动一位,以增大和的值;如果和大于目标值,则将 right 指针向左移动一位,以减小和的值。重复这个过程,直到找到符合条件的数字对或者 left 指针超过了 right 指针,表示遍历完成。
  4. **返回结果:**如果我们找到了符合条件的两个数,则返回它们的下标数组;如果遍历完成后仍然没有找到符合条件的数字对,则返回一个空数组,表示未找到满足条件的数字对。

通过双指针方法,我们可以在一次遍历的过程中就找到符合条件的数字对,大大提高了算法的效率。这个方法的时间复杂度为 O(nlogn),其中 n 是数组 nums 的长度,因为我们需要对数组进行排序。然后,双指针的遍历过程只需要一次遍历,因此时间复杂度为 O(n)。

两种方法的优缺点分析

哈希表解法:

优点:

  1. 时间复杂度低: 在平均情况下,哈希表的查找操作可以在常量时间内完成,因此哈希表解法的时间复杂度为 O(n),其中 n 是数组的长度。
  2. 适用性广泛: 哈希表适用于不需要对数组进行排序的情况,而且可以适用于更广泛的情况,例如需要找到所有满足条件的数字对。
  3. 易于实现: 哈希表的实现相对简单,只需要遍历一次数组并在哈希表中记录数字及其索引即可。

缺点:

  1. 额外空间消耗: 哈希表需要额外的空间来存储数字及其索引,可能会占用较多的内存空间,尤其是当数组中的数字比较大时。
  2. 不保证顺序: 哈希表无法保证数字对的顺序,因此返回的结果可能是任意顺序的。

双指针解法:

优点:

  1. 空间复杂度低: 双指针解法不需要额外的空间来存储数字及其索引,因此空间复杂度为 O(1)。
  2. 不需要额外空间: 由于不需要额外的空间,因此可以在空间有限的情况下使用,例如在嵌入式系统或内存受限的环境中。
  3. 保证顺序: 双指针解法保证返回的数字对是按照数组的顺序排列的。

缺点:

  1. 需要排序: 双指针解法需要对数组进行排序,时间复杂度为 O(nlogn),其中 n 是数组的长度。
  2. 不适用于所有情况: 双指针解法通常适用于已排序的数组或特定情况下的数组,例如需要找到满足特定条件的两个数的情况。如果数组无法排序或需要找到所有满足条件的数字对,则双指针解法可能不适用。

综上所述,哈希表解法适用于大多数情况下,尤其是需要找到所有满足条件的数字对时;而双指针解法适用于已排序的数组或需要保证返回顺序的情况下,且具有较低的空间复杂度。在实际应用中,可以根据具体问题的特点和需求选择合适的解法。

结语

无论选择哈希表解法还是双指针解法,刷题的过程本身就是一次提升算法能力的过程。通过不断地思考、实践和总结,我们能够逐渐掌握更多的解题技巧,提升解决问题的能力。同时,刷题也是一种锻炼思维和逻辑能力的过程,能够帮助我们更好地理解和应用算法和数据结构的知识。

在刷题的过程中,我们不仅可以学习到解决特定问题的方法,还能够培养良好的学习习惯和解决问题的思维方式。通过与他人交流、分享和讨论,我们能够加深对算法和数据结构的理解,提高编程水平,成为一名优秀的程序员。

因此,刷题不仅是为了应付面试或者提升职业发展,更是一种提升个人能力、追求卓越的过程。希望每位刷题者都能够坚持不懈,不断进步,不断挑战自我,在编程的路上越走越远! 诸君,共勉。