一、leetcode笔记前端-数组

1. 两数之和

题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

思路:最容易想到的方法是枚举数组中的每一个数 x,寻找数组中是否存在 target - x。
当我们使用遍历整个数组的方式寻找[target - x] 时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x。

解法1:暴力枚举

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
  for(let i=0;i<nums.length;i++){
    for(let j=0;j<nums.length;j++){
      if(nums[i]+nums[j]==target && i!=j){
        return [i, j]
      }
    }
  }
};

思路:遍历的同时可以记录一些信息,以省去一层循环,‘以空间换时间’,
需要记录已经遍历过的数值和他所对应的下标,可以借助查找表实现。

解法2:哈希表

var twoSum = function(nums, target) {
  let map = new Map()
  map.set(nums[0], 0)
  for(let i=1;i<nums.length;i++){
    if(map.has(target-nums[i])){
      return [map.get(target-nums[i]), i]
    }else{
      map.set(nums[i], i)
    }
  }
};

2. 26. 删除有序数组中的重复项

题目:给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。
将最终结果插入 nums 的前 k 个位置后返回 k 。
不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

思路:

/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
  if(!nums.length){return 0}
  let i=1
  for(let j=1;j<nums.length;j++){
    if(nums[j]!=nums[i-1]){
      nums[i]=nums[j]
      i++
    }
  }
  return i
};

3. 27. 移除元素

题目:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums =[2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0,
4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

解答:

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
  if(nums.length==0)return 0
  let i=0;
  for(let j=0; j<nums.length; j++){
    if(nums[j]!=val){
      nums[i]=nums[j]
      i++
    }
  }
  return i
};

4. 53. 最大子数组和

题目:
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
⚠️ 子数组 是数组中的一个连续部分。

示例:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6

贪心法

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let sum = 0  // 当前和
    let maxAns = nums[0] // 最大的和
    nums.forEach(item => {
        // 若当前指针所指元素之前的和 < 0,则丢弃之前的数列
        if(sum < 0) {
           sum = item
        } else {
        // 反之,则累加上之前的和
           sum += item
        }
        // 在存的最大和与当前和之前取较为大的值
        maxAns = Math.max(maxAns, sum)
    })
};

5. 35. 搜索插入位置

题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
⚠️ 请必须使用时间复杂度为 O(log n) 的算法。

示例1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例2:

输入: nums = [1,3,5,6], target = 2
输出: 1

解答:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var searchInsert = function(nums, target) {
  let len = nums.length
  for(let i=0;i<len;i++){
    if(nums[i] === target||nums[i]>target){
      return i;
    }
    if(i==len-1 && nums[i]<target){
      return len;
    }
  }
};

6. 88. 合并两个有序数组

题目:给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

示例:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6],其中斜体加粗标注的为 nums1 中的元素。

思路:

  1. 直接合并后排序:将数组直接合并,然后用sort排序,两行就可以完成,但这样没有利用到 两个数组 都是有序数组的条件,时间复杂度与空间复杂度都比较高,不建议采用;
  2. 双指针法:这一方法将两个数组看作队列,每次从两个数组头部取出比较小的数字放到结果中;
    复杂度分析
    时间复杂度:O(m+n)。
    指针移动单调递增,最多移动 m+n 次,因此时间复杂度为 O(m+n)。
    空间复杂度:O(m+n)。
    需要建立长度为 m+n 的中间数组sorted。
  3. 逆向双指针:2的方法多使用一个空间,因为sums1的后半部分是0(占位元素),所以可以直接覆盖,不会影响结果,因此指针可以从后向前遍历,取较大值放到最后,也就是以下的解答代码;
    复杂度分析
    时间复杂度:O(m+n)
    空间复杂度:O(1),直接对sums1进行修改,没有使用多余的空间。
    解答:
/**
 * @param {number[]} nums1
 * @param {number} m
 * @param {number[]} nums2
 * @param {number} n
 * @return {void} Do not return anything, modify nums1 in-place instead.
 */
var merge = function (nums1, m, nums2, n) {
    let cur = m + n - 1 //从nums1尾部开始
    while (cur >= 0) {
        if (n === 0) return //num2已经全部放入num1中了
        if (m < 1) {//num1指针先走完了
            nums1[cur--] = nums2[--n]
            continue
        }
        if (n < 1) {//num2指针先走完了
            nums1[cur--] = nums1[--m]
            continue
        }
        //取较大的插入 nums1 的末尾、更新对应的指针
        if (nums1[m - 1] > nums2[n - 1]) {
            nums1[cur--] = nums1[--m]
        } else {
            nums1[cur--] = nums2[--n]
        }
    }
};

7. 118. 杨辉三角

题目:给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
暂时无法在文档外展示此内容
示例:

输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

解答:

/**
 * @param {number} numRows
 * @return {number[][]}
 */
var generate = function (numRows) {
    const ret = []
    for (let i = 0; i < numRows; i++) {
        // 创建当前(二级)数组的首尾值 1
        const row = new Array(i + 1).fill(1)
        // 算出当前数组的中间的值
        for (let j = 1; j < row.length - 1; j++) {
            // cur = 左上方的值+右上方的值
            row[j] = ret[i - 1][j - 1] + ret[i - 1][j]
        }
        ret.push(row)
    }
    return ret;
};

8. 119. 杨辉三角 II *

题目:给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:

输入: rowIndex = 3
输出: [1,3,3,1]

解答1:和上题的解法一样,只是最后返回了数组的最后一行

/**
 * @param {number} rowIndex
 * @return {number[]}
 */
var getRow = function (rowIndex) {
    const ret = []
    for (let i = 0; i < rowIndex + 1; i++) {
        let row = new Array(i + 1).fill(1)
        for (let j = 1; j < row.length - 1; j++) {
            row[j] = ret[i - 1][j] + ret[i - 1][j - 1]
        }
        ret.push(row)
    }
    return ret[rowIndex]
};

分析:
这种做法占用了一整个杨辉三角的数组空间,可以进一步优化,只用到前一个数组的空间;

解答2:

/**
 * @param {number} rowIndex
 * @return {number[]}
 */
var getRow = function(rowIndex) {
    const row = new Array(rowIndex + 1).fill(0);
    row[0] = 1;
    for (let i = 1; i <= rowIndex; ++i) {
        row[i] = row[i - 1] * (rowIndex - i + 1) / i;
    }
    return row;
};

复杂度分析

  • 时间复杂度:O(rowIndex)。
  • 空间复杂度:O(1)。不考虑返回值的空间占用。

9. 217. 存在重复元素

题目:给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。
示例 1:

输入:nums = [1,2,3,1]
输出:true

示例 2:

输入:nums = [1,2,3,4]
输出:false

解答1:

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var containsDuplicate = function(nums) {
    let arr = [...new Set(nums)]
    let isExist = false
    if(arr.length != nums.length) {
        isExist = true
    }
    return isExist
};

10. 283. 移动零

题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

示例 2:

输入: nums = [0]
输出: [0]

解答1:

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var moveZeroes = function (nums) {
    let l = nums.length
    // 从后向前遍历,当前值为0时将其移动到最后
    for (let i = l - 1; i > -1; i--) {
        if (nums[i] == 0) {
            nums.splice(i, 1);
            nums.push(0);
        }
    }
    return nums
};

分析:提交之后耗时308ms,可能是因为使用了splice耗时较高;

解答2:

var moveZeroes = function (nums) {
    let j = 0;
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] !== 0) { // 遇到非0元素,让nums[j] = nums[i],然后j++
            nums[j] = nums[i];
            j++;
        }
    }
    for (let i = j; i < nums.length; i++) { // 剩下的元素全是0
        nums[i] = 0;
    }
    return nums;
};

分析:这个方法耗时88ms,操作都变成了赋值,缩短了运行时间;

11. 349. 两个数组的交集

题目:给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

解答:

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function (nums1, nums2) {
    let arr = []
    nums1.forEach(item => {
        if (nums2.includes(item)) { // 判断当前值是否存在于nums2中
            arr.push(item) // 存在则放到新数组
        }
    })
    return [...new Set(arr)] // 数组去重
};

12. 1002. 查找共用字符

题目:给你一个字符串数组 words ,请你找出所有在 words 的每个字符串中都出现的共用字符( 包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。
示例 1:

输入:words = [“bella”,“label”,“roller”]
输出:[“e”,“l”,“l”]

解答:

/**
 * @param {string[]} A
 * @return {string[]}
 */
const commonChars = (A) => {
      A.reduce(([...prev], [...next]) =>
        prev.filter(item => next.indexOf(item) > -1 && next.splice(next.indexOf(item), 1).length)
      )
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值