day4 数组的应用

113 阅读4分钟

来哟~做题吧

两数求和

题目描述:
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例: 给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]

第一时间思路:
【两层循环】: 第一层循环遍历的值记为 a,第二层循环时遍历的值记为 b;若 a+b = 目标值,那么 a 和 b 对应的数组下标就是我们想要的答案
反思:
两层循环 O(n^2)->性能问题->空间换时间->优化为一层循环?
优化做法:

记住一个结论:几乎所有的求和问题,都可以转化为求差问题

步骤:
① Map方法:遍历 —— {当前值:索引值}
② target - 当前值 = ? , 这个差值要和前面遍历过的数字找,如果前面出现过,那么我们找到答案了\

以 nums = [2, 7, 11, 15] 这个数组为例
① 第一个数 image.png ② 第二个数:差值发现是之前出现过的值 image.png

const twoSum = function (nums, target) {
    const diffs = {}
    // 缓存数组长度
    const len = nums.length
    // 遍历数组
    for (let i = 0; i < len; i++) {
        // 判断当前值对应的 target 差值是否存在(是否已遍历过)
        if (diffs[target - nums[i]] !== undefined) {
            // 若有对应差值,那么答案get!
            return [diffs[target - nums[i]], i]
        }
        // 若没有对应差值,则记录当前值
        diffs[nums[i]] = i
    }
};
console.log(twoSum([1,2,7,3,6],9))  // [1,2]

合并两个有序数组

题目描述:有序整数数组:nums1,nums2,将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明: 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

示例:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]

思路一:合并后排序
时间复杂度:O(n+m)*log(n+m)
空间复杂度:O(1)

思路二:双指针法\从后往前

推荐这个方法
时间复杂度:O(n+m)
空间复杂度:O(1)

双指针.gif

const merge = function (nums1, m, nums2, n) {
    // 初始化两个指针的指针,初始化 nums1 尾部索引k
    let i = m - 1,
        j = n - 1,
        k = m + n - 1
    // 当两个数组都没遍历完时,指针同步移动
    while (i >= 0 && j >= 0) {
        // 取较大的值,从末尾往前填补
        if (nums1[i] >= nums2[j]) {
            nums1[k] = nums1[i]
            i--
            k--
        } else {
            nums1[k] = nums2[j]
            j--
            k--
        }
    }

    // nums2 留下的情况,特殊处理一下 
    while (j >= 0) {
        nums1[k] = nums2[j]
        k--
        j--
    }
    return nums1
};

nums1 = [1, 2, 3, 0, 0, 0], m = 3
nums2 = [2, 5, 6], n = 3
console.log(merge(nums1, m, nums2, n))  // [ 1, 2, 2, 3, 5, 6 ]

三数求和

真题描述:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?
请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。

示例: 给定数组 nums = [-1, 0, 1, 2, -1, -4]
满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]

暴力解题:三重循环
时间复杂度:O(N^3)
空间复杂度:O(1)
008EC1C6.jpg

吸取第一题思路:把求和问题变成求差问题
双指针法-空间换时间,不过大前提是:数组必须有序!!
所以,首先排序

nums = nums.sort((a,b)=>{ 
    return a-b 
})

然后:对数组进行遍历,每次遍历到哪个数字,就固定哪个数字。然后把左指针指向该数字后面一个坑里的数字,把右指针指向数组末尾,让左右指针从起点开始,向中间前进

image.png

  • 相加之和大于0,说明右侧的数偏大了,右指针左移
  • 相加之和小于0,说明左侧的数偏小了,左指针右移
const threeSum = function (nums) {
    // 用于存放结果数组
    let res = []
    // 给 nums 排序
    nums = nums.sort((a, b) => {
        return a - b
    })
    // 缓存数组长度
    const len = nums.length
    // 注意我们遍历到倒数第三个数就足够了,因为左右指针会遍历后面两个数
    for (let i = 0; i < len - 2; i++) {
        // 左指针 j
        let j = i + 1
        // 右指针k
        let k = len - 1
        // 如果遇到重复的数字,则跳过
        if (i > 0 && nums[i] === nums[i - 1]) {
            continue
        }
        while (j < k) {
            // 三数之和小于0,左指针前进
            if (nums[i] + nums[j] + nums[k] < 0) {
                j++
                // 处理左指针元素重复的情况
                while (j < k && nums[j] === nums[j - 1]) {
                    j++
                }
            } else if (nums[i] + nums[j] + nums[k] > 0) {
                // 三数之和大于0,右指针后退
                k--

                // 处理右指针元素重复的情况
                while (j < k && nums[k] === nums[k + 1]) {
                    k--
                }
            } else {
                // 得到目标数字组合,推入结果数组
                res.push([nums[i], nums[j], nums[k]])

                // 左右指针一起前进
                j++
                k--

                // 若左指针元素重复,跳过
                while (j < k && nums[j] === nums[j - 1]) {
                    j++
                }

                // 若右指针元素重复,跳过
                while (j < k && nums[k] === nums[k + 1]) {
                    k--
                }
            }
        }
    }

    // 返回结果数组
    return res
};

const nums = [-1, 0, 1, 2, -1, -4]
console.log(threeSum(nums))

“对撞指针”

当出现两个关键词时:“有序”和“数组”,大脑马上出现“对撞指针”,即左右指针一起从两边向中间靠近。