Code Monkey home page Code Monkey logo

leetcode-hot-100's Introduction

感谢关注 Thanks for your attention ༼ つ ◕_◕ ༽つ

☀️ 个人优势

  • 丰富的多端 0-1 前后端开发经验,大型前后端开发重构经验,有 4-5 前端团队搭建敏捷项目管理,团队多人协同开发能力,可独立全栈开发
  • 熟练前端架构:qiankun微前端架构系统、vue3、taro、react、express/node、vite3、TypeScript/4、Ant-Design-Vue/3.2、element-plus/2.2、vue-i18n/9、mitt/3、axios、pinia/2、vue-router/4、tailwindcss/3、VueRequest、VueUse等主流技术开发,跨平台uniapp、flutter框架,移动端H5网页、PC、微信/支付宝 小程序、安卓/IOS App等。
  • 熟练后端架构:微服务、Maven 多模块管理、Java8、Spring、Spring Boot2、SpringMVC、Docker、RabbitMQ、kafka、Cassandra、Elasticsearch、Nacos(Nacos Config)服务注册中与配置中心、Ribbon负载均衡、OpenFeign服务调用、sentinel服务熔断、SpringCloud Gateway服务网关、SpringCloud、SpringCloud Alibaba、MyBatis、Mybatis-Plus、log4j、Nginx(负载均衡)、SpringSecurity、Redis、MySql、MongoDB、Zookeeper、Seata分布式事务、Skywalking链路追踪理等。
  • 熟练运维方面:熟悉Linux操作系统及常用命令,了解Docker,k8s等,Linux日常服务器操作管理。
  • 管理层经验4+年:团队技术选型、技术攻坚及管理工作,注重前端标准化,模块化,工程化,在部门内部标准落地,已落地电商、新能源、物联网、有声小说行业业务能力。
  • 微服务平台稳定生产了三年+,mysql主从模式,使用Netty、mqtt采集设备信息,k8s + jenkins的部署架构,采用Spring Boot 2.7 、Spring Cloud 2021 ,集成Minio分布式对象存储、Skywalking追踪监控、Sentinel从流量控制、熔断降级、系统负载等多个维度保护服务的稳定性,注册中心、配置中心选型Nacos,使用Traefik进行反向代理,借鉴OAuth2,实现了多终端认证系统,可控制子系统的token权限互相隔离,借鉴Security,封装了Secure模块,采用JWT做Token认证,可拓展集成Redis等细颗粒度控制方案,完全遵循阿里巴巴编码规范,实现SaaS多租户微服务平台。
  • 深入理解 Vue,并研究过其内部实现,并输出 JS LeetCode 算法解决方案(小册),刷题近500道题。
  • 喜欢分享,推动团队内部技术分享与复盘总结,工作认真负责,注重代码质量,工作效率。

汇总

我喜欢交朋友。所以如果你想打个招呼,我会很高兴见到你更多! 😊

  • 💬 我的微信号: xiaoda0423⚡ 👉 如果你有问题提出: click
  • 🤔 坚持的趣事: 终身学习 common Snippets 坚持运动,阅读
  • JavaGuideInterview学习 每日 3+1,以面试题来驱动学习,提倡每日学习与思考,每天进步一点!每天早上纯手工发布(死磕自己,愉悦大家)准备 Java 面试,首选 JavaGuideInterview!面试题大收集,面试集锦 ❤ 💝 💘
  • WebGuideInterview学习 每日 3+1,以面试题来驱动学习,提倡每日学习与思考,每天进步一点!每天早上纯手工发布(死磕自己,愉悦大家)准备 前端 面试,首选 WebGuideInterview!面试题大收集,面试集锦 ❤ 💝 💘

开源

📚 小书

封面 书名 简述 成就
《Webpack学习笔记》 《Webpack学习笔记》 Webpack学习笔记 badge badge
《前端进阶JavaScript标准库》 《前端进阶JavaScript》 前端进阶 JavaScript 标准库 badge badge
《腾讯精选练习 50 题》 《腾讯精选练习 50 题》 力扣 (LeetCode) 🐧 腾讯精选练习 50 题 badge badge
《Bytedance-campus-59-Leetcode》 《字节校园 59》 力扣 (LeetCode) 🐿️ 字节校园 算法与数据结构 badge badge
《LeetCode-HOT-100》 《LeetCode HOT 100》 力扣 (LeetCode) 🔥LeetCode HOT 100 badge badge
《数据结构与算法概念与实现》 《数据结构与算法》 哪吒的数据结构与算法 badge badge
《场景代码题-API实现原理-实战题目》 《手写代码》 ✨✨✨ 集锦 前端JavaScript 手写题,编程题,Not just for interviews badge badge
项目 Github 简述 技术 成就
【uui】 Github 🖖 uui组件库 badge badge badge badge badge
【file-breakpoint-continue】 Github 🐬 实现大文件上传,断点续传等 badge badge badge badge
【express-node】 Github 🌙 express-node-mysql-react badge badge badge
【awesome-css】 Github 🍉 国内css平台从业者交流 badge badge badge badge
【nice-my-friend】 Github ♻️ 关注人数列表数据 badge badge badge
【awesome-stars-webVueBlog】 Github 🤩 我的star列表 badge badge badge
【promise】 Github 🐧 Promises/A+ 实现 badge badge badge
【mini-vue】 Github 🖖 mini-vue badge badge badge
数据总览 常用开发语言

leetcode-hot-100's People

Contributors

webvueblog avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

yinwenbing

leetcode-hot-100's Issues

17. 电话号码的字母组合

17. 电话号码的字母组合

Description

Difficulty: 中等

Related Topics: 哈希表, 字符串, 回溯

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入:digits = ""
输出:[]

示例 3:

输入:digits = "2"
输出:["a","b","c"]

提示:

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。

Solution

Language: JavaScript

/**
 * @param {string} digits
 * @return {string[]}
 */

var letterCombinations = function (digits) {
    if (digits.length === 0) return []
    const res = []
    const map = ['', '', 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']
    const dfs = (curStr, i) => {
        if (i > digits.length - 1) {
            res.push(curStr)
            return
        }
        const letters = map[digits[i]]
        for (const l of letters) {
            dfs(curStr + l, i + 1)
        }
    }
    dfs('', 0)
    return res
}

4. 寻找两个正序数组的中位数

4. 寻找两个正序数组的中位数

Description

Difficulty: 困难

Related Topics: 数组, 二分查找, 分治

给定两个大小分别为 mn 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

提示:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106

Solution

Language: JavaScript

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var findMedianSortedArrays = function(nums1, nums2) {
    const newNums = nums1.concat(nums2).sort((a, b) => a - b)
    const len = newNums.length
    if (len % 2 === 0) {
        return (newNums[len/2 - 1] + newNums[len/2]) / 2
    } else {
        return newNums[Math.floor(len/2)]
    }
}

39. 组合总和

39. 组合总和

Description

Difficulty: 中等

Related Topics: 数组, 回溯

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 _所有 _不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每个元素都 互不相同
  • 1 <= target <= 500

Solution

Language: JavaScript

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
// 搜索回溯
var combinationSum = function(candidates, target) {
    const ans = []
    const dfs = (target, item, idx) => {
        if (idx === candidates.length) return
        if (target === 0) {
            ans.push(item)
            return
        }
        dfs(target, item, idx + 1)
        if (target - candidates[idx] >= 0) {
            dfs(target - candidates[idx], [...item, candidates[idx]], idx)
        }
    }
    dfs(target, [], 0)
    return ans
}

105. 从前序与中序遍历序列构造二叉树

105. 从前序与中序遍历序列构造二叉树

Description

Difficulty: 中等

Related Topics: , 数组, 哈希表, 分治, 二叉树

给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorder 和 inorder 均 无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

Solution

Language: JavaScript

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */
// 前序遍历: 根-左-右
// 中序遍历:左-根-右
// 先用前序遍历找根节点,在用根节点去中序遍历确定左右子树
var buildTree = function(preorder, inorder) {
    // 当preorder和inorder均为空的时候说明已经到了空节点
    if (!preorder.length || !inorder.length) return null

    // 创建根节点 -> preorder[0]
    let node = new TreeNode(preorder[0])

    // 找到perorder[0]对应inorder中的位置
    let index = inorder.indexOf(preorder.shift())

    // 左右子树递归
    node.left = buildTree(preorder, inorder.slice(0, index))
    node.right = buildTree(preorder, inorder.slice(index + 1))

    // 返回根节点
    return node
}

79. 单词搜索

79. 单词搜索

Description

Difficulty: 中等

Related Topics: 数组, 回溯, 矩阵

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true

示例 2:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
输出:true

示例 3:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
输出:false

提示:

  • m == board.length
  • n = board[i].length
  • 1 <= m, n <= 6
  • 1 <= word.length <= 15
  • boardword 仅由大小写英文字母组成

**进阶:**你可以使用搜索剪枝的技术来优化解决方案,使其在 board 更大的情况下可以更快解决问题?

Solution

Language: JavaScript

/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
// 记录该元素已使用
// 上下左右不能超边界
// 如果没找到最终值的时候恢复上一个节点的值
var exist = function(board, word) {
    // 越界处理
    board[-1] = [] // 这里处理比较巧妙,利用了js的特性
    board.push([])
    // 寻找首个字母
    for (let y = 0; y < board.length; y++) {
        for (let x = 0; x < board[0].length; x++) {
            if (word[0] === board[y][x] && dfs(word, board, y, x, 0)) return true
        }
    }
    return false
}

const dfs = function(word, board, y, x, i) {
    if (i + 1 === word.length) return true
    var tmp = board[y][x]
    // 标记该元素已使用
    board[y][x] = false
    if (board[y][x+1] === word[i+1] && dfs(word, board, y, x+1, i+1)) return true
    if (board[y][x-1] === word[i+1] && dfs(word, board, y, x-1, i+1)) return true
    if (board[y+1][x] === word[i+1] && dfs(word, board, y+1, x, i+1)) return true
    if (board[y-1][x] === word[i+1] && dfs(word, board, y-1, x, i+1)) return true
    // 回溯
    board[y][x] = tmp
}

136. 只出现一次的数字

136. 只出现一次的数字

Description

Difficulty: 简单

Related Topics: 位运算, 数组

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1

示例 2:

输入: [4,1,2,1,2]
输出: 4

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @return {number}
 */
// var singleNumber = function(nums) {
//     let ans = 0;
//     for(const num of nums) {
//         ans ^= num;
//     }
//     return ans;
// };

var singleNumber = function(nums) {
    return nums.reduce((pre, cur) => {
        return pre ^ cur;
    }, 0)
}

78. 子集

78. 子集

Description

Difficulty: 中等

Related Topics: 位运算, 数组, 回溯

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
// 递归法实现子集枚举
// var subsets = function (nums) {
//     let result = []
//     let path = []
//     function backtracking(startIndex) {
//         result.push(path.slice())
//         for (let i = startIndex; i < nums.length; i++) {
//             path.push(nums[i])
//             backtracking(i + 1)
//             path.pop()
//         }
//     }
//     backtracking(0)
//     return result
// }

// DFS回溯思路
var subsets = function (nums) {
    const res = []
    const dfs = (index, list) => {
        res.push(list.slice())
        for (let i = index; i < nums.length; i++) {
            list.push(nums[i])
            dfs(i+1, list)
            list.pop()
        }
    }
    dfs(0, [])
    return res
}

102. 二叉树的层序遍历

102. 二叉树的层序遍历

Description

Difficulty: 中等

Related Topics: , 广度优先搜索, 二叉树

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000]
  • -1000 <= Node.val <= 1000

Solution

Language: JavaScript

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
// 队列
var levelOrder = function(root) {
    const ans = []
    if (!root) return ans
    const q = []
    q.push(root) // 初始化队列
    while (q.length) {
        const len = q.length // 当前层节点的数量
        ans.push([]) // 新的层推入数组
        for (let i = 1; i <= len; i++) {
            const node = q.shift()
            // 推入当前层的数组
            ans[ans.length - 1].push(node.val)
            // 检查左节点,存在左节点就继续加入队列
            if (node.left) q.push(node.left)
            if (node.right) q.push(node.right)
        }
    }
    return ans
}

72. 编辑距离

72. 编辑距离

Description

Difficulty: 困难

Related Topics: 字符串, 动态规划

给你两个单词 word1 和 word2请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例 2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

提示:

  • 0 <= word1.length, word2.length <= 500
  • word1word2 由小写英文字母组成

Solution

Language: JavaScript

/**
 * @param {string} word1
 * @param {string} word2
 * @return {number}
 */
// dp[i][j]是word1,word2的最小编辑距离
var minDistance = function(word1, word2) {
    let m = word1.length
    let n = word2.length
    const dp = new Array(m+1).fill(0).map(() => new Array(n+1).fill(0))
    // 初始化
    for (let i = 1; i <= m; i++) {
        dp[i][0] = i
    }
    for (let j = 1; j <= n; j++) {
        dp[0][j] = j
    }
    for (let i = 1; i <= m; i++) {
        for (let j = 1; j <= n; j++) {
            if (word1[i-1] === word2[j-1]) {
                dp[i][j] = dp[i-1][j-1]
            } else {
                dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
            }
        }
    }
    return dp[m][n]
}

42. 接雨水

42. 接雨水

Description

Difficulty: 困难

Related Topics: , 数组, 双指针, 动态规划, 单调栈

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105

Solution

Language: JavaScript

/**
 * @param {number[]} height
 * @return {number}
 */
// 动态规划
var trap = function(height) {
    const n = height.length
    const left = new Array(n).fill(0)
    const right = new Array(n).fill(0)
    let ans = 0
    for (let i = 1; i < n; i++) {
        left[i] = Math.max(left[i-1], height[i-1])
    }
    for (let i = n - 2; i >= 0; i--) {
        right[i] = Math.max(right[i+1], height[i+1])

        let short = Math.min(left[i], right[i])
        if (short > height[i]) {
            ans += short - height[i]
        }
    }
    return ans
}

94. 二叉树的中序遍历

94. 二叉树的中序遍历

Description

Difficulty: 简单

Related Topics: , , 深度优先搜索, 二叉树

给定一个二叉树的根节点 root ,返回 它的 中序 遍历

示例 1:

输入:root = [1,null,2,3]
输出:[1,3,2]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

提示:

  • 树中节点数目在范围 [0, 100]
  • -100 <= Node.val <= 100

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

Solution

Language: JavaScript

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
// 栈
var inorderTraversal = function(root) {
    const res = []
    let stack = []
    while (root || stack.length) {
        while (root) {
            stack.push(root)
            root = root.left
        }
        root = stack.pop()
        res.push(root.val)
        root = root.right
    }
    return res
}

22. 括号生成

22. 括号生成

Description

Difficulty: 中等

Related Topics: 字符串, 动态规划, 回溯

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入:n = 1
输出:["()"]

提示:

  • 1 <= n <= 8

Solution

Language: JavaScript

/**
 * @param {number} n
 * @return {string[]}
 */
// 回溯法(DFS)
var generateParenthesis = function(n) {
    const res = []
    const backtrack = (left, right, str) => {
        if (left === n && right === n) return res.push(str)
        if (left < n) backtrack(left + 1,  right, str + '(')
        if (right < left) backtrack(left, right + 1, str + ')')
    }
    backtrack(0, 0, '')
    return res
}

1. 两数之和

1. 两数之和

Description

Difficulty: 简单

Related Topics: 数组, 哈希表

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

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

示例 3:

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

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

**进阶:**你可以想出一个时间复杂度小于 O(n2) 的算法吗?

Solution

Language: JavaScript

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

21. 合并两个有序链表

21. 合并两个有序链表

Description

Difficulty: 简单

Related Topics: 递归, 链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1l2 均按 非递减顺序 排列

Solution

Language: JavaScript

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 * @return {ListNode}
 */
// 递归
// var mergeTwoLists = function(list1, list2) {
//     if (!list1) {
//         return list2
//     } else if (!list2) {
//         return list1
//     } else if (list1.val < list2.val) {
//         list1.next = mergeTwoLists(list1.next, list2)
//         return list1
//     } else {
//         list2.next = mergeTwoLists(list1, list2.next)
//         return list2
//     }
// }

// 迭代
var mergeTwoLists = function(list1, list2) {
    const dummy = new ListNode()
    let curr = dummy
    while (list1 && list2) {
        list1.val < list2.val
            ? [curr.next, list1] = [list1, list1.next]
            : [curr.next, list2] = [list2, list2.next]
        curr = curr.next
    }
    curr.next = list1 ?? list2
    return dummy.next
}

128. 最长连续序列

128. 最长连续序列

Description

Difficulty: 中等

Related Topics: 并查集, 数组, 哈希表

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n)的算法解决此问题。

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @return {number}
 */
// 1.将数组元素存入set中
// 2.遍历nums,如果当前项-1存在于set,说明当前项不是连续序列的起点,忽略,继续遍历
// 3.如果当前项没有“左邻居”,它就是连续序列的起点,循环查看当前项连续的右邻居有多少个
// 4.返回最长的连续次数
var longestConsecutive = function (nums) {
    // 把数组的数字全部放入set中,一来去重,二来方便快速查找
    const set = new Set(nums)
    let max = 0
    for (let val of nums) {
        // 没有左邻居,是序列的起点,才开始数数
        if (!set.has(val - 1)) {
            let count = 1
            let now = val
            // 有右邻居,看连续的右邻居右多少个
            while (set.has(now + 1)) {
                now++
                count++
            }
            // 存放最大的连续邻居的值
            max = Math.max(max, count)
        }
    }
    return max
}

20. 有效的括号

20. 有效的括号

Description

Difficulty: 简单

Related Topics: , 字符串

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

提示:

  • 1 <= s.length <= 104
  • s 仅由括号 '()[]{}' 组成

Solution

Language: JavaScript

/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function(s) {
    if (s.length % 2 !== 0) return false
    const map = {
        '(' : ')',
        '{' : '}',
        '[' : ']',
    }
    let stack = []
    for (let char of s) {
        if (map[char]) {
            stack.push(map[char])
        } else {
            let ret = stack.pop()
            if (ret !== char) {
                return false   
            }
        }
    }
    return stack.length === 0
}

64. 最小路径和

64. 最小路径和

Description

Difficulty: 中等

Related Topics: 数组, 动态规划, 矩阵

给定一个包含非负整数的 _m_ x _n_ 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

**说明:**每次只能向下或者向右移动一步。

示例 1:

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

示例 2:

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

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 100

Solution

Language: JavaScript

/**
 * @param {number[][]} grid
 * @return {number}
 */
var minPathSum = function(grid) {
    if (grid === null || grid.length === 0 || grid[0].length === 0) {
        return 0
    }

    let row = grid.length, col = grid[0].length

    const dp = new Array(row).fill(0).map(() => new Array(col).fill(0))
    dp[0][0] = grid[0][0]

    for (let i = 1; i < row; i++) {
        dp[i][0] = dp[i-1][0] + grid[i][0]
    }

    for (let j = 1; j < col; j++) {
        dp[0][j] = dp[0][j-1] + grid[0][j]
    }

    for (let i = 1; i < row; i++) {
        for (let j = 1; j < col; j++) {
            dp[i][j] = Math.min(dp[i][j-1], dp[i-1][j]) + grid[i][j]
        }
    }

    return dp[row-1][col-1]
}

32. 最长有效括号

32. 最长有效括号

Description

Difficulty: 困难

Related Topics: , 字符串, 动态规划

给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

示例 1:

输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"

示例 2:

输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"

示例 3:

输入:s = ""
输出:0

提示:

  • 0 <= s.length <= 3 * 104
  • s[i]'('')'

Solution

Language: JavaScript

/**
 * @param {string} s
 * @return {number}
 */
// 定义 dp[i] 表示以下标 i 字符结尾的最长有效括号的长度。
/**
)()())

push: 一个参照物;左括号

pop:左括号,一个参照物

保持栈不能为空

[-1]
 */
const longestValidParentheses = (s) => {
    let n = s.length
    let res = 0
    let stack = [-1]
    for (let i = 0; i < n; i++) {
        let str = s[i]
        if (str === '(') {
            stack.push(i)
        } else {
            stack.pop()
            if (stack.length) {
                res = Math.max(res, i - stack[stack.length - 1])
            } else {
                stack.push(i)
            }
        }
    }
    return res
};

31. 下一个排列

31. 下一个排列

Description

Difficulty: 中等

Related Topics: 数组, 双指针

整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。

  • 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1]

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2]
  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2]
  • arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。

给你一个整数数组 nums ,找出 nums 的下一个排列。

必须修改,只允许使用额外常数空间。

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
// a[i-1] a[j]
var nextPermutation = function(nums) {
    const len = nums.length
    let i = len - 2
    // 找到第一个当前项比后一项小的位置
    while (i >= 0 && nums[i] >= nums[i+1]) i--
    // i>=0,说明此时不是最大的排序
    if (i >= 0) {
        let j = len - 1
        // 找到最小的比nums[i]大的数对应的j
        while (j > i && nums[i] >= nums[j]) j--
        // 交换位置
        [nums[i], nums[j]] = [nums[j], nums[i]]
    }
    // i后面的数升序排序
    let [left, right] = [i + 1, len - 1]
    while (left < right) {
        [nums[left], nums[right]] = [nums[right], nums[left]]
        left++
        right--
    }
}

53. 最大子数组和

53. 最大子数组和

Description

Difficulty: 中等

Related Topics: 数组, 分治, 动态规划

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104

**进阶:**如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @return {number}
 */
// 动态规划
var maxSubArray = function(nums) {
    let pre = 0
    let maxAns = nums[0]
    nums.forEach((x) => {
        pre = Math.max(pre + x, x)
        maxAns = Math.max(maxAns, pre)
    })
    return maxAns
}

55. 跳跃游戏

55. 跳跃游戏

Description

Difficulty: 中等

Related Topics: 贪心, 数组, 动态规划

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

提示:

  • 1 <= nums.length <= 3 * 104
  • 0 <= nums[i] <= 105

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @return {boolean}
 */
// 贪心
// var canJump = function(nums) {
//     // 如果只有一个,返回true
//     if (nums.length === 1) return true
//     // 记录所能到达的最大位置
//     var res = nums[0]
//     for (let i = 1; i < nums.length; i++) {
//         // 如果最大位置不能到达当前位置,则跳出
//         if (res < i) break
//         // 如果最大位置超过最后一个位置,则返回true
//         if (res >= nums.length - 1) return true
//         // 计算能到达的最大位置
//         res = res > nums[i] + i ? res : nums[i] + i
//     }
//     return false
// }

// 贪心
var canJump = function(nums) {
    let n = nums.length - 1
    let maxLen = 0
    for (let i = 0; i <= maxLen; i++) {
        maxLen = Math.max(maxLen, nums[i] + i)
        if (maxLen >= n) return true
    }
    return false
}

// 动态规划
// var canJump = function(nums) {
//     let end = nums.length - 1

//     for (let i = nums.length - 2; i >= 0; i--) {
//         if (end - i <= nums[i]) {
//             end = i
//         }
//     }

//     return end === 0
// }

49. 字母异位词分组

49. 字母异位词分组

Description

Difficulty: 中等

Related Topics: 数组, 哈希表, 字符串, 排序

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = ["a"]
输出: [["a"]]

提示:

  • 1 <= strs.length <= 104
  • 0 <= strs[i].length <= 100
  • strs[i] 仅包含小写字母

Solution

Language: JavaScript

/**
 * @param {string[]} strs
 * @return {string[][]}
 */
var groupAnagrams = function(strs) {
    const map = new Map()
    for (let str of strs) {
        let array = Array.from(str)
        array.sort()

        let key = array.toString()
        let list = map.get(key) ? map.get(key) : []
        list.push(str)

        map.set(key, list)
    }
    return Array.from(map.values())
};

121. 买卖股票的最佳时机

121. 买卖股票的最佳时机

Description

Difficulty: 简单

Related Topics: 数组, 动态规划

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 104

Solution

Language: JavaScript

/**
 * @param {number[]} prices
 * @return {number}
 */
// 买入卖出各一次
//  贪心
// var maxProfit = function(prices) {
//     if (prices.length === 0) return 0

//     let min = prices[0]
//     let max = 0

//     for (let p of prices) {
//         min = Math.min(min, p)
//         max = Math.max(max, p - min)
//     }

//     return max
// }

// 动态规划 时间复杂度O(n) 空间复杂度O(n),dp数组第二维是常数
var maxProfit = function(prices) {
    let n = prices.length
    let dp = new Array(n).fill(0).map(() => new Array(2));

    dp[0][0] = 0
    dp[0][1] = -prices[0]

    for (let i = 1; i < n; i++) {
        dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i])
        dp[i][1] = Math.max(dp[i-1][1], - prices[i])
    }

    return dp[n-1][0]
}

// 压缩空间
// var maxProfit = function(prices) {
//     let n = prices.length
//     let dp = Array.from(new Array(n), () => new Array(2))

//     dp[0] = 0
//     dp[1] = -prices[0]

//     for (let i = 1; i < n; i++) {
//         dp[0] = Math.max(dp[0], dp[1] + prices[i])
//         dp[1] = Math.max(dp[1], -prices[i])
//     }
//     return dp[0]
// }

19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点

Description

Difficulty: 中等

Related Topics: 链表, 双指针

给你一个链表,删除链表的倒数第 n个结点,并且返回链表的头结点。

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

**进阶:**你能尝试使用一趟扫描实现吗?

Solution

Language: JavaScript

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 * 快慢指针法
 */
var removeNthFromEnd = function(head, n) {
    const dummy = new ListNode(-1, head)
    let [slow, fast] = [dummy, dummy]

    for (let i = 0; i <= n; i++) {
        fast = fast.next
    }

    while (fast) {
        slow = slow.next
        fast = fast.next
    }

    slow.next = slow.next.next

    return dummy.next
}

23. 合并K个升序链表

23. 合并K个升序链表

Description

Difficulty: 困难

Related Topics: 链表, 分治, 堆(优先队列), 归并排序

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4

Solution

Language: JavaScript

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 * 递归合并链表+两两合并
 */
var mergeKLists = function(lists) {
    if (!lists.length) return null
    function merge(l1, l2) {
        if (!l1) return l2
        if (!l2) return l1
        if (l1.val < l2.val) {
            l1.next = merge(l1.next, l2)
            return l1
        } else {
            l2.next = merge(l1, l2.next)
            return l2
        }
    }
    return lists.reduce((pre, cur) => merge(pre, cur))
}

84. 柱状图中最大的矩形

84. 柱状图中最大的矩形

Description

Difficulty: 困难

Related Topics: , 数组, 单调栈

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例 1:

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]
输出: 4

提示:

  • 1 <= heights.length <=105
  • 0 <= heights[i] <= 104

Solution

Language: JavaScript

/**
 * @param {number[]} heights
 * @return {number}
 */
// 分治法
// 从高度值最小的高度的最大面积 (end-start+1) * height[min],将该面积与高度最小的柱子左边和右边形成的最大面积来比较。分治的最后是每一个矩形的面积。

const largestRectangleArea = (heights) => {
    let maxArea = 0
    const stack = [] // 单调递增栈 注意栈存的是下标
    heights = [0, ...heights, 0] // 在heights数组前后增加哨兵 用来清零单调递增栈里的元素
    for (let i = 0; i < heights.length; i++) {
        // 当前元素对应的高度小于栈顶元素对应的高度时
        while (heights[i] < heights[stack[stack.length - 1]]) {
            const stackTopIndex = stack.pop() // 出栈
            maxArea = Math.max( // 计算面积 并更新最大面积
                maxArea,
                heights[stackTopIndex] * (i - stack[stack.length - 1] - 1) // 高乘宽
            )
        }
        stack.push(i) // 当前下标加入栈
    }
    return maxArea
}

75. 颜色分类

75. 颜色分类

Description

Difficulty: 中等

Related Topics: 数组, 双指针, 排序

给定一个包含红色、白色和蓝色、共 n个元素的数组 nums ,对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 12 分别表示红色、白色和蓝色。

必须在不使用库的sort函数的情况下解决这个问题。

示例 1:

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]

示例 2:

输入:nums = [2,0,1]
输出:[0,1,2]

提示:

  • n == nums.length
  • 1 <= n <= 300
  • nums[i]012

进阶:

  • 你可以不使用代码库中的排序函数来解决这道题吗?
  • 你能想出一个仅使用常数空间的一趟扫描算法吗?

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @return {void}
 * 
 */
// 遇到0,把0放在数组最前方,此时i位置没变,不用处理
// 遇到2,把2放在数组最后放,此时i向后偏移了1,需要进行i--,然后数组末尾的2已经被统计过了,所以len可以对应再减1,避免重复的问题

var sortColors = function(nums) {
    let len = nums.length
    for (let i = 0; i < len; i++) {
        if (nums[i] === 0) {
            nums.splice(i, 1)
            nums.unshift(0)
        } else if (nums[i] === 2) {
            nums.splice(i, 1)
            nums.push(2)
            len--
            i--
        }
    }
}

139. 单词拆分

139. 单词拆分

Description

Difficulty: 中等

Related Topics: 字典树, 记忆化搜索, 哈希表, 字符串, 动态规划

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s

**注意:**不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
     注意,你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • swordDict[i] 仅有小写英文字母组成
  • wordDict 中的所有字符串 互不相同

Solution

Language: JavaScript

/**
 * @param {string} s
 * @param {string[]} wordDict
 * @return {boolean}
 */
// 动态规划
// dp[i]: 子串s[0, i]是否满足,为boolean
// dp[i]初始化:除了dp[0]为true,其他为false
// 从0到n遍历字符串,判断当前dp[i]含义是否满足
// 在判断当前dp[i]中,从0到i遍历分隔点j
// 1.若[0,j]满足,且剩下的在wordDict中出现过,则当前[0,i]子串就满足
// 2.若当前[0,i]子串满足,就不用继续遍历当前子串的分隔点了,跳过,判断下一轮子串[0, i+1]
const wordBreak = (s, wordDict) => {
    const n = s.length
    const set = new Set(wordDict)
    // dp初始化,除了第一个,全为false
    const dp = new Array(n + 1).fill(false)
    dp[0] = true

    // 从1到n遍历s
    for (let i = 1; i <= n; i++) {
        // 当遍历到i时,再从0到i遍历
        for (let j = 0; j < i; j++) {
            // 当前i所处的子串是否可以,取决于分隔点j
            // 对于分隔点j,若[0,j]满足,且剩下的在wordDict中出现过,则当前[0,i]子串就满足
            if (dp[j] && set.has(s.slice(j , i))) {
                dp[i] = true
                // 若当前[0, i] 子串满足,就不用继续遍历当前子串的分隔点了
                // 跳过,判断下一轮子串[0,i+1]
                break
            }
        }
    }
    return dp[n]
}

124. 二叉树中的最大路径和

124. 二叉树中的最大路径和

Description

Difficulty: 困难

Related Topics: , 深度优先搜索, 动态规划, 二叉树

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和

示例 1:

输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6

示例 2:

输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42

提示:

  • 树中节点数目范围是 [1, 3 * 104]
  • -1000 <= Node.val <= 1000

Solution

Language: JavaScript

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
// 最大路径和为根节点+左子树+右子树的深度最大值
// 当前节点往下走的最大值分三种情况:
// 1. 往左走:当前节点+L(左子树的最大深度)
// 2. 往右走:当前节点+R(右子树的最大深度)
// 3. 不走:当前节点如果为负值,直接舍弃返回0
var maxPathSum = function(root) {
    let ans = -Infinity
    dfs(root)
    return ans

    // 从当前节点往下走
    function dfs(root) {
        // 根节点为空直接返回0
        if (!root) return 0
        // 遍历左子树的深度
        let left = dfs(root.left)
        // 遍历右子树的深度
        let right = dfs(root.right)
        // 更新路径的最大值
        ans = Math.max(ans, root.val + left + right)
        // 返回从当前节点往下走的最大值
        return Math.max(0, Math.max(left, right) + root.val)
    }
}

48. 旋转图像

48. 旋转图像

Description

Difficulty: 中等

Related Topics: 数组, 数学, 矩阵

给定一个 _n _× n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2:

输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

提示:

  • n == matrix.length == matrix[i].length
  • 1 <= n <= 20
  • -1000 <= matrix[i][j] <= 1000

Solution

Language: JavaScript

/**
 * @param {number[][]} matrix
 * @return {void} Do not return anything, modify matrix in-place instead.
 */
// 使用辅助数组
// 对于矩阵中第 i 行的第 j 个元素,在旋转后,它出现在倒数第 i 列的第 j 个位置。
// matrix[row][col] matrix_new[col][n-row-1]
var rotate = function(matrix) {
    const n = matrix.length
    const matrix_new = new Array(n).fill(0).map(() => new Array(n).fill(0))

    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            matrix_new[j][n-1-i] = matrix[i][j]
        }
    }

    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            matrix[i][j] = matrix_new[i][j]
        }
    }
}

5. 最长回文子串

5. 最长回文子串

Description

Difficulty: 中等

Related Topics: 字符串, 动态规划

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

Solution

Language: JavaScript

/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function(s) {
    let max = ''
    
    for (let i = 0; i < s.length; i++) {
        helper(i, i)
        helper(i, i+1)
    }

    function helper(l, r) {
        while(l >= 0 && r < s.length && s[l] === s[r]) {
            l--
            r++
        }
        const maxStr = s.slice(l + 1, r - 1 + 1)
        if (maxStr.length > max.length) max = maxStr
    }

    return max
}

98. 验证二叉搜索树

98. 验证二叉搜索树

Description

Difficulty: 中等

Related Topics: , 深度优先搜索, 二叉搜索树, 二叉树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

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

示例 2:

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

提示:

  • 树中节点数目范围在[1, 104] 内
  • -231 <= Node.val <= 231 - 1

Solution

Language: JavaScript

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
// 栈
var isValidBST = function(root) {
    let stack = []
    let inorder = -Infinity
    while (root || stack.length) {
        while (root) {
            stack.push(root)
            root = root.left
        }
        root = stack.pop()
        if (root.val <= inorder) {
            return false
        }
        inorder = root.val
        root = root.right
    }
    return true
}

15. 三数之和

15. 三数之和

Description

Difficulty: 中等

Related Topics: 数组, 双指针, 排序

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @return {number[][]}
 */

var threeSum = function(nums) {
    let ans = []
    const len = nums.length
    if (nums === null || len < 3) return ans
    nums.sort((a, b) => a - b)

    for (let i = 0; i < nums.length; i++) {
        if (nums[0] > 0) break
        if (i > 0 && nums[i] === nums[i - 1]) continue
        let l = i + 1
        let r = len - 1
        while (l < r) {
            const sum = nums[i] + nums[l] + nums[r]
            if (sum === 0) {
                ans.push([nums[i], nums[l], nums[r]])
                while(l < r && nums[l] === nums[l+1]) l++
                while(l < r && nums[r] === nums[r-1]) r--
                l++
                r--
            }
            else if (sum < 0) l++
            else if (sum > 0) r--
        }
    }

    return ans
}

70. 爬楼梯

70. 爬楼梯

Description

Difficulty: 简单

Related Topics: 记忆化搜索, 数学, 动态规划

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1\. 1 阶 + 1 阶
2\. 2 阶

示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1\. 1 阶 + 1 阶 + 1 阶
2\. 1 阶 + 2 阶
3\. 2 阶 + 1 阶

提示:

  • 1 <= n <= 45

Solution

Language: JavaScript

/**
 * @param {number} n
 * @return {number}
 * 题目分析:
 * 第1级台阶:1种方法(爬1级)
 * 第2级台阶:2种方法(爬1级或爬2级)
 * 第n级台阶:dp[i] = dp[i-1] + dp[i-2]
 */
// 动态规划
// 滚动数组**
// var climbStairs = function(n) {
//     if (n === 1) return 1
//     if (n === 2) return 2
//     let [pre, cur, sum] = [1, 1, 2]
//     for (let i = 3; i <= n; i++) {
//         [pre, cur] = [cur, sum]
//         sum = pre + cur
//     }
//     return sum
// }

var climbStairs = function(n) {
    const dp = []
    dp[0] = 1
    dp[1] = 1
    for (let i = 2; i <= n; i++) {
        dp[i] = dp[i-1] + dp[i-2]
    }
    return dp[n]
}

85. 最大矩形

85. 最大矩形

Description

Difficulty: 困难

Related Topics: , 数组, 动态规划, 矩阵, 单调栈

给定一个仅包含 01 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例 1:

输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:6
解释:最大矩形如上图所示。

示例 2:

输入:matrix = []
输出:0

示例 3:

输入:matrix = [["0"]]
输出:0

示例 4:

输入:matrix = [["1"]]
输出:1

示例 5:

输入:matrix = [["0","0"]]
输出:0

提示:

  • rows == matrix.length
  • cols == matrix[0].length
  • 1 <= row, cols <= 200
  • matrix[i][j]'0''1'

Solution

Language: JavaScript

/**
 * @param {character[][]} matrix
 * @return {number}
 */
var maximalRectangle = function(matrix) {
    if (matrix.length === 0) return 0

    let res = 0
    let heights = new Array(matrix[0].length).fill(0) // 初始化heights数组
    for (let row = 0; row < matrix.length; row++) {
        for (let col = 0; col < matrix[0].length; col++) {
            if (matrix[row][col] === '1') heights[col] += 1
            else heights[col] = 0
        } // 求出每一层的 heights[] 然后传给 largestRectangleArea 函数
        res = Math.max(res, largestRectangleArea(heights)) // 更新一下最大矩形面积
    }
    return res
};

const largestRectangleArea = (heights) => {
    let maxArea = 0
    const stack = [] // 单调递增栈 注意栈存的是下标
    heights = [0, ...heights, 0] // 在 heights 数组前后增加两个哨兵 用来清零单调递增栈里的元素
    for (let i = 0; i < heights.length; i++) {
        // 当前元素对应的高度小于栈顶元素对应的高度时
        while (heights[i] < heights[stack[stack.length - 1]]) {
            const stackTopIndex = stack.pop() // 出栈
            maxArea = Math.max(
                maxArea,
                heights[stackTopIndex] * (i - stack[stack.length - 1] - 1) // 高乘宽
            )
        }
        stack.push(i) // 当前下标加入栈
    }
    return maxArea
}

3. 无重复字符的最长子串

3. 无重复字符的最长子串

Description

Difficulty: 中等

Related Topics: 哈希表, 字符串, 滑动窗口

给定一个字符串 s ,请你找出其中不含有重复字符的 **最长子串 **的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

Solution

Language: JavaScript

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    const set = new Set()
    let [l, r, maxLen] = [0, 0, 0]
    while(r < s.length) {
        set.has(s[r]) ? set.delete(s[l++]) : set.add(s[r++])
        maxLen = Math.max(maxLen, set.size)
    }
    return maxLen
}

33. 搜索旋转排序数组

33. 搜索旋转排序数组

Description

Difficulty: 中等

Related Topics: 数组, 二分查找

整数数组 nums 按升序排列,数组中的值 互不相同

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:

输入:nums = [1], target = 0
输出:-1

提示:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • nums 中的每个值都 独一无二
  • 题目数据保证 nums 在预先未知的某个下标上进行了旋转
  • -104 <= target <= 104

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 * 二分搜索
 * 关键字:排序,搜索
 */
var search = function(nums, target) {
    let [left, right] = [0, nums.length - 1]

    while (left <= right) {
        const mid = (left + right) >>> 1
        if (nums[mid] === target) return mid

        if (nums[mid] > nums[right]) {
            if (nums[left] <= target && nums[mid] > target) right = mid - 1
            else left = mid + 1
        } else {
            if (nums[mid] < target && nums[right] >= target) left = mid + 1
            else right = right - 1
        }
    }
    return -1
}

96. 不同的二叉搜索树

96. 不同的二叉搜索树

Description

Difficulty: 中等

Related Topics: , 二叉搜索树, 数学, 动态规划, 二叉树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

示例 1:

输入:n = 3
输出:5

示例 2:

输入:n = 1
输出:1

提示:

  • 1 <= n <= 19

Solution

Language: JavaScript

/**
 * @param {number} n
 * @return {number}
 */
// 数学
var numTrees = function(n) {
    let res = 1
    for (let i = 1; i < n; i++) {
        res = res * 2 * (2 * i + 1) / (i + 2)
    }
    return res
}

76. 最小覆盖子串

76. 最小覆盖子串

Description

Difficulty: 困难

Related Topics: 哈希表, 字符串, 滑动窗口

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"

示例 2:

输入:s = "a", t = "a"
输出:"a"

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

  • 1 <= s.length, t.length <= 105
  • st 由英文字母组成

**进阶:**你能设计一个在 o(n) 时间内解决此问题的算法吗?

Solution

Language: JavaScript

/**
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
// 将需要匹配的字符放入字典表,存储结构为需要匹配的字符,以及字符出现的次数:[字符,次数]
// 利用双指针维护一个滑动窗口,不断移动右指针
// 判断右指针的字符是否与字典表中的匹配
// 是:将字典表中的次数-1,直到为0 (创建一个needType)记录需要匹配的字符数量,初始长度为Map的size,当对应的字符次数为0时,就减1
// 否:继续移动右指针
// 当needType的值为0时,就证明在当前窗口所有字符都匹配成功了
// 1.移动左指针,缩小滑动窗口的大小
// 2.移动过程中判断左指针指向的值是否为字典中值,如果是就证明匹配的值少了一个,这个需要更新Map中的次数,以及needType的数量
// 3.记录每次窗口中的字符,找到最小的返回
var minWindow = function(s, t) {
    // 创建左指针
    let l = 0
    // 创建右指针
    let r = 0
    // 最后需要返回的最小长度子串
    let res = ''
    // 创建字典表
    const m = new Map()
    // 遍历需要匹配的字符
    for (let i = 0; i < t.length; i++) {
        const c = t[i]
        // 放入字典表
        m.set(c, m.has(c) ? m.get(c) + 1 : 1)
    }
    // 创建记录需要匹配的字符种类
    let needType = m.size
    // 遍历字符串
    while (r < s.length) {
        // 获取当前字符
        const c = s[r]
        // 如果是需要匹配的字符
        if (m.has(c)) {
            // 更新字典表中的次数-1
            m.set(c, m.get(c) - 1)
            // 如果次数为0,证明这个字符种类在当前窗口已经集齐了,needType-1
            if (m.get(c) === 0) needType -= 1
        }
        // 当needType为0,证明所有需要匹配的字符串都已经在当前滑动窗口中
        while (needType === 0) {
            const c2 = s[l]
            // 记录当前滑动窗口的字符
            let newRes = s.slice(l, r + 1)
            // 当新的窗口中字符长度小于上次的字符长度时,更新结果
            // !res 是在结构值为空的时候需要更新一下第一次匹配的值
            if (!res || newRes.length < res.length) res = newRes
            // 如果左指针移动过程中出现,字典中的值证明需要匹配的字符已经脱离了当前窗口
            if (m.has(c2)) {
                // 更新表中需要出现的次数
                m.set(c2, m.get(c2) + 1)
                // 更新needType
                if (m.get(c2) === 1) needType += 1
            }
            // 移动左指针
            l++
        }
        // 移动右指针
        r++
    }
    // 返回结果值
    return res
}

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

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

Description

Difficulty: 中等

Related Topics: 数组, 二分查找

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var searchRange = function(nums, target) {
    let [start, end] = [0, nums.length - 1]

    while (start <= end) {
        const mid = (start + end) >>> 1
        if (nums[mid] === target) {
            [start, end] = [mid, mid]
            break
        }
        nums[mid] > target ? (end = mid - 1) : (start = mid + 1)
    }

    while (nums[start - 1] === target) start --
    while (nums[end + 1] === target) end++

    return start > end ? [-1, -1] : [start, end]
}

114. 二叉树展开为链表

114. 二叉树展开为链表

Description

Difficulty: 中等

Related Topics: , , 深度优先搜索, 链表, 二叉树

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null
  • 展开后的单链表应该与二叉树 顺序相同。

示例 1:

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

示例 2:

输入:root = []
输出:[]

示例 3:

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

提示:

  • 树中结点数在范围 [0, 2000]
  • -100 <= Node.val <= 100

**进阶:**你可以使用原地算法(O(1) 额外空间)展开这棵树吗?

Solution

Language: JavaScript

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {void} Do not return anything, modify root in-place instead.
 */
// 1. 先用两个变量把原先的左右子树保持起来
// 2. 将左子树作为右子树
// 3. 将原先的右子树接到当前右子树的末端
// 递归
var flatten = function(root) {
    // 递归终止
    if (root === null) return

    // 分别递归左右节点
    const left = root.left
    const right = root.right
    flatten(left)
    flatten(right)

    // 将树的right节点替换为left节点
    root.right = root.left
    root.left = null

    // 循环 找到最右的节点
    while (root.right) root = root.right
    // 最右的节点 指向right节点
    root.right = right
}

2. 两数相加

2. 两数相加

Description

Difficulty: 中等

Related Topics: 递归, 链表, 数学

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

  • 每个链表中的节点数在范围 [1, 100]
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

Solution

Language: JavaScript

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var addTwoNumbers = function(l1, l2) {
    let dummy = new ListNode()
    let curr = dummy
    let carry = 0

    while(l1 || l2) {
        const x = l1 ? l1.val : 0
        const y = l2 ? l2.val : 0

        const total = x + y + carry
        curr.next = new ListNode(total % 10)
        curr = curr.next
        carry = Math.floor(total / 10)

        if (l1) l1 = l1.next
        if (l2) l2 = l2.next
    }
    if (carry) curr.next = new ListNode(carry)

    return dummy.next
}

46. 全排列

46. 全排列

Description

Difficulty: 中等

Related Topics: 数组, 回溯

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

Solution

Language: JavaScript

/**
 * @param {number[]} nums
 * @return {number[][]}
 * 广度优先遍历
 * 深度优先遍历
 */
var permute = function(nums) {
    const ans = []
    const dfs = (item = []) => {
        if (item.length === nums.length) return ans.push([...item])

        nums.forEach(num => {
            if (!item.includes(num)) {
                dfs([...item, num])
            }
        })
    }
    dfs()
    return ans
}

104. 二叉树的最大深度

104. 二叉树的最大深度

Description

Difficulty: 简单

Related Topics: , 深度优先搜索, 广度优先搜索, 二叉树

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

Solution

Language: JavaScript

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function(root) {
    let res = 0
    function traversal (root, depth) {
        if (root) {
            if (depth > res) {
                res = depth
            }
            if (root.left) {
                traversal (root.left, depth + 1)
            }
            if (root.right) {
                traversal (root.right, depth + 1)
            }
        }
    }
    traversal(root, 1)
    return res
}

10. 正则表达式匹配

10. 正则表达式匹配

Description

Difficulty: 困难

Related Topics: 递归, 字符串, 动态规划

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

  • '.' 匹配任意单个字符
  • '*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 **整个 **字符串 s的,而不是部分字符串。

示例 1:

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。

提示:

  • 1 <= s.length <= 20
  • 1 <= p.length <= 30
  • s 只包含从 a-z 的小写字母。
  • p 只包含从 a-z 的小写字母,以及字符 . 和 *
  • 保证每次出现字符 * 时,前面都匹配到有效的字符

Solution

Language: JavaScript

/**
 * @param {string} s
 * @param {string} p
 * @return {boolean}
 */
var isMatch = function(s, p) {
    p = new RegExp(p)
    s = s.replace(p, '')
    return s === '' ? true : false
}

62. 不同路径

62. 不同路径

Description

Difficulty: 中等

Related Topics: 数学, 动态规划, 组合数学

一个机器人位于一个 m x n网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

示例 1:

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1\. 向右 -> 向下 -> 向下
2\. 向下 -> 向下 -> 向右
3\. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 109

Solution

Language: JavaScript

/**
 * @param {number} m
 * @param {number} n
 * @return {number}
 * 动态规划 每一格的路径由其上一格和左一格决定。
 * 动态方程:dp[i][j] = dp[i-1][j] + dp[i][j-1]
 * 注意:对于第一行 dp[0][j],或者第一列 dp[i][0],由于都是在边界,所以只能为1
 * 建立m*n的矩阵,注意第0行和第0列元素均为1
 */
var uniquePaths = function(m, n) {
    const dp = new Array(m).fill(0).map(() => new Array(n).fill(0))

    for (let i = 0; i < m; i++) {
        dp[i][0] = 1
    }
    for (let j = 0; j < n; j++) {
        dp[0][j] = 1
    }

    for (let i = 1; i < m; i++) {
        for (let j = 1; j < n; j++) {
            dp[i][j] = dp[i-1][j] + dp[i][j-1]
        }
    }
    return dp[m-1][n-1]
}

56. 合并区间

56. 合并区间

Description

Difficulty: 中等

Related Topics: 数组, 排序

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

提示:

  • 1 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 104

Solution

Language: JavaScript

/**
 * @param {number[][]} intervals
 * @return {number[][]}
 */

var merge = function (intervals) {
    let res = []
    intervals.sort((a, b) => a[0] - b[0])

    let prev = intervals[0]

    for (let i = 1; i < intervals.length; i++) {
        let cur = intervals[i]
        if (prev[1] >= cur[0]) {
            prev[1] = Math.max(prev[1], cur[1])
        } else {
            res.push(prev)
            prev = cur
        }
    }

    res.push(prev)
    return res
}

11. 盛最多水的容器

11. 盛最多水的容器

Description

Difficulty: 中等

Related Topics: 贪心, 数组, 双指针

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

**说明:**你不能倾斜容器。

示例 1:

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例 2:

输入:height = [1,1]
输出:1

提示:

  • n == height.length
  • 2 <= n <= 105
  • 0 <= height[i] <= 104

Solution

Language: JavaScript

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
    let [l, r, ans] = [0, height.length - 1, 0]

    while (l < r) {
        let [hl, hr] = [height[l], height[r]]
        ans = Math.max(ans, Math.min(hl, hr) * (r - l))

        hl < hr ? l++ : r--
    }
    return ans
}

101. 对称二叉树

101. 对称二叉树

Description

Difficulty: 简单

Related Topics: , 深度优先搜索, 广度优先搜索, 二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

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

示例 2:

输入:root = [1,2,2,null,3,null,3]
输出:false

提示:

  • 树中节点数目在范围 [1, 1000]
  • -100 <= Node.val <= 100

**进阶:**你可以运用递归和迭代两种方法解决这个问题吗?

Solution

Language: JavaScript

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function(root) {
    if (!root) return true
    const check = (left, right) => {
        if (!left && !right) return true
        if (!left || !right) return false
        return left.val === right.val && check(left.left, right.right) && check(left.right, right.left)
    }
    return check(root.left, root.right)
}

141. 环形链表

141. 环形链表

Description

Difficulty: 简单

Related Topics: 哈希表, 链表, 双指针

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。**注意:pos 不作为参数进行传递 **。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos-1 或者链表中的一个 有效索引

**进阶:**你能用 O(1)(即,常量)内存解决此问题吗?

Solution

Language: JavaScript

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
// 快慢指针
// 快慢指针初始化指向 head
// 快指针走到末尾时停止
// 慢指针走一步,快指针走两步
// 快慢指针相遇,说明含有环,否则不含有环
var hasCycle = function(head) {
    let slow = head
    let fast = head
    while (fast && fast.next) {
        slow = slow.next
        fast = fast.next.next
        if (slow === fast) {
            return true
        }
    }
    return false
}

// 哈希表
// var hasCycle = function(head) {
//     const map = new Map()
//     let p = head
//     while (p) {
//         if (map.get(p)) return true
//         else {
//             map.set(p, p.val)
//             p = p.next
//         }
//     }
//     return false
// }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.