Giter Club home page Giter Club logo

leetcode-javascript's Introduction

☀️leetcode-javascript

HitCount License GitHub forks GitHub starsAuthor

🌱仓库地址

传送门:https://github.com/Chocolate1999/leetcode-javascript

📢公告

感谢访问本站,若喜欢请收藏,star支持一下 ✿✿ヽ(°▽°)ノ✿

思维导图还在建设完善中ing

⛄笔记网站(更新)

https://yangchaoyi.vip/ (基于hexo-butterfly主题搭建的博客,整理面试记录)

https://yangchaoyi.vip/iBook/ (基于vuepress搭建的前端学习笔记,目前还在建设当中)

为了让您更好的阅读本仓库笔记,考虑还是建了个人网站,本仓库内容陆续都会更新到网站上,你可以根据分类找到你想阅读的文章,隆重感谢给本仓库点 Star 的小伙伴!

💡前言

本仓库刷题路线参考 ssh (给大佬点赞)

仓库地址:https://github.com/sl1673495/leetcode-javascript

感谢大佬的归纳总结,原本打算在大佬那里打卡学习,后面考虑不太友好,还是自己新建了一个仓库打卡学习。

其次,本仓库解题代码大部分是自己的代码风格,题量也进行了拓展,将会持续更新下去,何不star收藏一下?

📌仓库介绍

本仓库将全程使用的语言是 JavaScript,是一个纯前端刷题路线,对于前端刷题没有方向的小伙伴简直是福音。解题代码会记录在本仓库的 Issues 中,会按照 label 进行分类。比如想查看 「递归与回溯」 分类下的问题,那么选择标签进行筛选即可。

同时,小伙伴们可以在 Issues 中提交自己的解题代码,🤝 欢迎 Contributing ,可打卡刷题,坚持下来的人最酷!Give a ⭐️ if this project helped you !

座右铭: 学如逆水行舟,不进则退

【亡羊补牢】挑战数据结构与算法专栏博客地址

最后,介绍本仓库的刷题专题系列了,一共会分为这些模块:

专题目录 链表篇、栈和队列篇、哈希表篇、二叉树篇、字典树、并查集、常见排序和查找算法、回溯算法、动态规划、贪心算法、LRU和LFU、字符串和正则篇

内容还是很丰富的,仓库也会不断完善,让我们一起加油修炼内功~

💝贡献

欢迎对改善本仓库提出好的意见,也欢迎 Contributor,谢谢!

将会持续更新 LeetCode 刷题路线,从零到拿到满意的Offer!觉得不错,来个Star!

🌻版权申明

博客中的每篇文章都是我一字一句敲出来的,如需转载,请附上原文链接,表示对原作者的尊重。谢谢合作!

📃排版

笔记内容按照 中文文案排版指北进行排版。

🏆结尾

欢迎关注微信公众号:小狮子前端

谢谢您的支持!✿✿ヽ(°▽°)ノ✿

注:本仓库不参与商业行为,也请各位读者周知。(This warehouse is not involved in commercial activities.)

转载使用请注明出处,谢谢!

如果有一天,你的努力配得上你的梦想,那么你的梦想也绝对不会辜负你的努力。

License

MIT

leetcode-javascript's People

Contributors

chocolate1999 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  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  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  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  avatar

leetcode-javascript's Issues

LeetCode 37. 解数独

仰望星空的人,不应该被嘲笑

题目描述

编写一个程序,通过填充空格来解决数独问题。

一个数独的解法需遵循如下规则

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。


一个数独。


答案被标成红色。

提示:

给定的数独序列只包含数字 1-9 和字符 '.' 
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

我们一行一行的放,如果能得到一个解,直接返回 true,然后剪枝条件如下述 check函数。

参考xiao_ben_zhu大佬图解

var solveSudoku = function (board) {
  let check = (x, y, val) => {
    // 一行或者一列有重复元素,剪掉
    for (let i = 0; i < 9; i++) {
      if (board[x][i] == val || board[i][y] == val) return true;
    }
    let xx = Math.floor(x / 3) * 3;
    let yy = Math.floor(y / 3) * 3;
    // 3x3宫格内重复的情况,剪掉
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        if (board[xx + i][yy + j] == val) return true;
      }
    }
    return false; // 没有冲突情况
  }
  let dfs = (x, y) => {
    if (y == 9) {
      x++;
      y = 0;
      if (x == 9) return true; // 都填完了,直接返回 true
    }
    if (board[x][y] != '.') return dfs(x, y + 1);
    for (let i = 1; i < 10; i++) {
      if (check(x, y, String(i))) continue;
      board[x][y] = String(i);
      if (dfs(x, y + 1)) return true; // 如果往下走,能够解出数独,直接返回 true
      board[x][y] = '.'; // 回溯,因为往下走得不到一个解
    }
    return false;
  }
  dfs(0, 0);
  return board;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 79. 单词搜索

仰望星空的人,不应该被嘲笑

题目描述

给定一个二维网格和一个单词,找出该单词是否存在于网格中。

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

示例:

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

给定 word = "ABCCED", 返回 true
给定 word = "SEE", 返回 true
给定 word = "ABCB", 返回 false

提示:

board  word 中只包含大写和小写英文字母。
1 <= board.length <= 200
1 <= board[i].length <= 200
1 <= word.length <= 10^3

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-search
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

上一期做了单词搜索2 hard 版本之后,这道题也想用字典树玩一玩,没想到超时了,后面一想,数据确实有点大,而且对于一个单词来说,建立一颗字典树岂不是很浪费,还要花时间码代码...

本题也是回溯的思路,不过期间做了一点小优化,还是通过动态更改当前所走的格子,省去了那份 开辟vis 数组的空间。

对于递归层次,由于最后一次计算时,层次多算了一次(即多加了一次),所以条件为 >

var exist = function(grid, word) {
  let dfs = (x,y,t) => {
    // 最后一次还会 +1 因此,条件是大于
    if(t > word.length-1){
      return true
    }
    // 剪枝条件
    if(x<0 || x>=grid.length || y<0 || y>=grid[0].length || grid[x][y]!= word[t] || grid[x][y] == '#') return false
    let tmp = grid[x][y]
    // 开始走
    grid[x][y] = '#'
    // 从四个方向搜索,只要一个方向搜索有结果,那么直接返回 true即可
    let res = dfs(x+1,y,t+1) || dfs(x,y+1,t+1) || dfs(x-1,y,t+1) || dfs(x,y-1,t+1)
    if(res) return true
    // 回溯(重置)
    grid[x][y] = tmp
    return false
  }
  for(let i=0;i<grid.length;i++){
    for(let j=0;j<grid[0].length;j++){
      if(grid[i][j] == word[0]){
        let res = dfs(i,j,0)
        if(res) return true
      }
    }
  }
  return false
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 784. 字母大小写全排列

仰望星空的人,不应该被嘲笑

题目描述

给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。

示例:

输入:S = "a1b2"
输出:["a1b2", "a1B2", "A1b2", "A1B2"]

输入:S = "3z4"
输出:["3z4", "3Z4"]

输入:S = "12345"
输出:["12345"]
 

提示:

S 的长度不超过12。
S 仅由数字和字母组成。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/letter-case-permutation
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

这道题就是递归操作,没有回溯,是一个挺有意思的题目,在讲解思路之前,我先搬运一下大佬的图解,方便我后续补充。

参考大佬 liweiwei1419 图解

第一步

第二步

第三步

第四步


第五步

第六步

好了,有了上述图解之后(还是感谢大佬的图解,万分感谢orz),我相信明白的已经明白了,如果不明白我继续解释。

此题我们只需要从头往后遍历一遍即可,对于非字母节点,我们只会产生一个分支,而对于字母节点,我们可以产生两个分支,即大写字母和小写字母。(详细请参见下述代码)

于是,我们只要简单搜一遍就可以了。

/**
 * @param {string} S
 * @return {string[]}
 */
var letterCasePermutation = function(S) {
    let res = []
    let dfs = (t,str) => {
        if(t.length === S.length)
            return res.push(t)
        let ch = str[0]
        let nextStr = str.substr(1)
        // 当前位置为数字,只有一个分支
        if(!isNaN(Number(ch))){
            dfs(t+ch,nextStr)
        }else{
            //当前位置为字母,会产生两个分支
            let tmp = ch.toUpperCase()
            if(tmp === ch) tmp = ch.toLowerCase()
            dfs(t+ch,nextStr)
            dfs(t+tmp,nextStr)
        }
    }
    dfs('',S)
    return res
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 77. 组合

仰望星空的人,不应该被嘲笑

题目描述

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combinations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

直接套用组合题解题模板即可

var combine = function (n, k) {
  let res = [];
  let dfs = (t, start) => {
    if (t.length === k) {
      res.push(t);
      return;
    }
    for (let i = start; i <= n; i++) {
      t.push(i);
      dfs(t.slice(), i + 1);
      t.pop();
    }
  }
  dfs([], 1);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 54. 螺旋矩阵

仰望星空的人,不应该被嘲笑

题目描述

给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

示例 1:

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

示例 2:

输入:
[
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/spiral-matrix
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

上一期 螺旋矩阵差不多,这个是让我么输出,而上次是让我们构造,还是按照螺旋矩阵模拟即可,先从左到右,在从上到下,再从右到左,再从下到上。

不过这里的矩阵行和列不相同了,可能会出现不成环的情况,那么最后会留一列或一行出来,这里借用大佬一张图:


然后我们需要提前跳出去一下,就是避免重复计算,总数够了直接跳出去。注意下面代码 break。只能放在那里,因为遍历顺序,如果最后留下一行的话,需要从左到右遍历,此时 top > bottom 。如果最后留下一列的话,需要从上到下遍历,此时 left > right

/**
 * @param {number[][]} matrix
 * @return {number[]}
 */
var spiralOrder = function(matrix) {
    if(!matrix.length) return []
    let n = matrix.length
    let m = matrix[0].length
    let total = n*m
    let top = 0,bottom = n-1
    let left = 0,right = m-1
    let res = []
    while(res.length < total){
        for(let i=left;i<=right;i++) res.push(matrix[top][i]) // 从左到右
        top++
        for(let i=top;i<=bottom;i++) res.push(matrix[i][right]) // 从上到下
        right--
        /* 因为n 和 m 不相同的时候,最后可能会留一列或一行,避免重复计算,总数够了直接跳出去 */
        if(res.length === total) break
        for(let i=right;i>=left;i--) res.push(matrix[bottom][i]) // 从右到左
        bottom--
        for(let i=bottom;i>=top;i--) res.push(matrix[i][left]) // 从下到上
        left++
    }
    return res
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 450. 删除二叉搜索树中的节点

仰望星空的人,不应该被嘲笑

题目描述

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。

示例:

root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7

给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。

一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。

    5
   / \
  4   6
 /     \
2       7

另一个正确答案是 [5,2,6,null,4,null,7]

    5
   / \
  2   6
   \   \
    4   7

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/delete-node-in-a-bst
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

对于这道题,我们必须先了解一下二叉搜索树(BST)的性质,如下:

BST性质

  • 中序遍历是升序
  • left小于当前节点,right大于当前节点
  • 左子树、右子树也要是BST

了解了性质之后,我们知道要查找对应 key 值,可以与根节点进行比较,如果小于根节点,直接去左子树找就好了,如果大于根节点,直接去右子树找就好了。

而对于刚好等于根节点的话,我拿着大佬的图解来看看几种情况:

第一种情况,如果删除节点仅有右孩子,直接指向右孩子

第二种情况,如果删除节点仅有左孩子,直接指向左孩子

第三种情况,如果删除节点左右孩子都有,那么我们按照题意可以有两种删除方法:

① 找到要删除节点左子树的最右边的节点,即前驱的最大值(由BST性质得到)替换当前 root 节点,然后删除这个前驱

② 找到要删除节点右子树的最左边的节点,即后继最小值(由BST性质得到)替换当前 root 节点,然后删除这个后继

参考 lulu 大佬图解

代码提供了两种解法,对于前两种情况处理是一样的,只是对于最后一种情况,可以删除前驱的最大值,或者删除后继的最小值。

删除前驱的最大值代码

/**
 * 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
 * @param {number} key
 * @return {TreeNode}
 */
var deleteNode = function (root, key) {
    if (!root) return null;
    // 判断值是否小于root,小于走左子树,大于走右子树
    if (key < root.val) {
        root.left = deleteNode(root.left, key);
    } else if (key > root.val) {
        root.right = deleteNode(root.right, key);
    } else {
        // 1.如果删除节点没有左右子树,直接删除即可
        if (!root.left && !root.right) {
            root = null;
        // 2.如果删除节点仅有左孩子,直接指向左孩子
        } else if (root.left && !root.right) {
            root = root.left;
        // 3.如果删除节点仅有右孩子,直接指向右孩子
        } else if (!root.left && root.right) {
            root = root.right;
        } else {
            // 4.如果左右孩子都有,本代码采用方式是将前驱的最大值替换root的值
            let last = root.left;
            while (last.right) {
                last = last.right;
            }
            root.val = last.val;
            // 然后删除这个前驱最大值节点即可
            root.left = deleteNode(root.left, last.val);
        }
    }
    return root;
};

删除后继的最小值代码

/**
 * 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
 * @param {number} key
 * @return {TreeNode}
 */
var deleteNode = function (root, key) {
    if (!root) return null;
    // 判断值是否小于root,小于走左子树,大于走右子树
    if (key < root.val) {
        root.left = deleteNode(root.left, key);
    } else if (key > root.val) {
        root.right = deleteNode(root.right, key);
    } else {
        // 1.如果删除节点没有左右子树,直接删除即可
        if (!root.left && !root.right) {
            root = null;
        // 2.如果删除节点仅有左孩子,直接指向左孩子
        } else if (root.left && !root.right) {
            root = root.left;
        // 3.如果删除节点仅有右孩子,直接指向右孩子
        } else if (!root.left && root.right) {
            root = root.right;
        } else {
            // 4.如果左右孩子都有,本代码采用方式是将后继的最小值替换root的值
            let last = root.right;
            while (last.left) {
                last = last.left;
            }
            root.val = last.val;
            // 然后删除这个后继最小值节点即可
            root.right = deleteNode(root.right, last.val);
        }
    }
    return root;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 901. 股票价格跨度

仰望星空的人,不应该被嘲笑

题目描述

编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。

今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。

例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]

示例:

输入:["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
输出:[null,1,1,1,2,1,4,6]
解释:
首先,初始化 S = StockSpanner(),然后:
S.next(100) 被调用并返回 1
S.next(80) 被调用并返回 1
S.next(60) 被调用并返回 1
S.next(70) 被调用并返回 2
S.next(60) 被调用并返回 1
S.next(75) 被调用并返回 4
S.next(85) 被调用并返回 6
注意 (例如) S.next(75) 返回 4,因为截至今天的最后 4 个价格
(包括今天的价格 75) 小于或等于今天的价格。

提示:

调用 StockSpanner.next(int price) 时,将有 1 <= price <= 10^5
每个测试用例最多可以调用  10000  StockSpanner.next。
在所有测试用例中,最多调用 150000  StockSpanner.next。
此问题的总时间限制减少了 50%

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/online-stock-span
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

正如题意,我们要求当前元素之前,比自己小(可以相等)的元素个数,并且元素个数包括本身,那么我们最后的结果应该还要加1.

于是按题意,我们采用跨度法,举个例子,对于例子6,1,2,3,4,9,从后往前逆推一下,当我们新插入9的时候,如果发现前一位的4比9小,那么是否说明比9小的数量就等于比4小的数量加1?然而这是错的,因为首位的6比9小,却比4大,因此截止数字的4时候,比4小的数量中并不包含6与9的对比。

因此,我们还要跳到 6 的位置再去计算小于等于自己的元素。

var StockSpanner = function() {
    // 存储股票跨度
    this.spanner = []
    // 存储股票价格
    this.stockPrice = []
};

/** 
 * @param {number} price
 * @return {number}
 */
StockSpanner.prototype.next = function(price) {
    // 对于第一天进行特殊判断
    if(!this.spanner.length){
        this.spanner.push(1)
        this.stockPrice.push(price)
        // 直接返回1
        return 1
    }
    let cnt = 0
    let idx = this.stockPrice.length-1
    while(price >= this.stockPrice[idx] && idx>=0){
        cnt += this.spanner[idx]
        idx -= this.spanner[idx]
    }
    // 加上本身
    cnt++
    // 进行更新操作,将当前股票价格和跨度入栈
    this.spanner.push(cnt)
    this.stockPrice.push(price)
    return cnt
};

/**
 * Your StockSpanner object will be instantiated and called as such:
 * var obj = new StockSpanner()
 * var param_1 = obj.next(price)
 */

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 47. 全排列 II

仰望星空的人,不应该被嘲笑

题目描述

给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

本题是求全排列,并且排列不能重复。我们用一个 vis数组维护一下,让每一条路线保证不重复选取元素,而对于每一层而言,需要判断相邻元素是否相同,相同的就没必要走了,例如下图中红色三角形部分。


果当前的选项 nums[i] ,与同一层的上一个选项 nums[i - 1] 相同,且 nums[i - 1]有意义(即索引 >= 0),且没有被使用过,那就跳过该选项。

因为 nums[i - 1]如果被使用过,它会被修剪掉,不是一个选项了,即便它和 nums[i]重复,nums[i]还是可以选的。

参考xiao_ben_zhu大佬题解

var permuteUnique = function(nums) {
    let res = [];
    nums.sort((a,b) => a-b);
    let vis = {};
    let dfs = (t) => {
      if(t.length === nums.length){
        res.push(t);
      }
      for(let i=0;i<nums.length;i++){
        if(i-1>=0 && nums[i] == nums[i-1] && !vis[i-1]) continue;
        if(vis[i]) continue;
        vis[i] = true;
        t.push(nums[i]);
        dfs(t.slice(),i+1);
        t.pop();
        vis[i] = false;
      }
    }
    dfs([],0);
    return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 257. 二叉树的所有路径

仰望星空的人,不应该被嘲笑

题目描述

给定一个二叉树,返回所有从根节点到叶子节点的路径。

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

示例:

输入:

   1
 /   \
2     3
 \
  5

输出: ["1->2->5", "1->3"]

解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-paths
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

dfs,从根节点开始搜,对于非叶子节点,进行累计,如果找到了叶子节点,我们就将结果存起来。通过字符串拼接来存储路径。

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {string[]}
 */
var binaryTreePaths = function (root) {
    if (root == null) return [];
    let res = [];
    let dfs = (cur, root) => {
        // 叶子节点,存起来
        if (root.left == null && root.right == null) {
            cur += root.val;
            res.push(cur);
            return;
        }
        cur += root.val + '->'; // 处理非叶子节点
        // 先遍历左子树,再遍历右子树
        root.left && dfs(cur, root.left);
        root.right && dfs(cur, root.right);
    }
    dfs('', root);
    return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 216. 组合总和 III

仰望星空的人,不应该被嘲笑

题目描述

找出所有相加之和为 nk 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

所有数字都是正整数。
解集不能包含重复的组合。
示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

首先,还是搬运一下大佬的图解,然后我再来解释一番。

参考xiao_ben_zhu大佬图解

本题需要一层一层来,第一层我们可以有 i(1-9)个选择,而第二层的每一个值只有 i+1个选择了,因为不能重复。比如你第一次拿了 2,在下一次,你只能从 3开始拿了,如果还是 1的话就会有重复的组合了。这样我们也不用维护 vis数组来去重,因为每一层取的值是不一样的。

var combinationSum3 = function (k, n) {
  let res = [];
  let dfs = (t, start, sum) => {
    if (t.length === k && sum === n) {
      res.push(t);
    }
    for (let i = start; i < 10; i++) {
      t.push(i);
      dfs(t.slice(), i + 1, sum + i);
      t.pop();
    }
  }
  dfs([], 1, 0);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 131. 分割回文串

仰望星空的人,不应该被嘲笑

题目描述

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例:

输入: "aab"
输出:
[
  ["aa","b"],
  ["a","a","b"]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-partitioning
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

借鉴 zesong-wang-c 大佬的图解

本题采用回溯**,看上图基本已经明白,每次进行一次切割,直到切到最后一个元素,然后压入结果集合里,期间对于每次切割的字符串,我们判断一下是否是回文,如果不是,直接减掉即可。

和组合的**有点类似。

// 判断是否是回文
function isPal(str) {
  let len = Math.floor(str.length / 2);
  if (len === 0) {
    return true;
  }
  let add = str.length % 2 === 0 ? 0 : 1;
  let subStr = str.slice(0, len);
  for (let i = 0; i < len; i++) {
    if (subStr[len - i - 1] !== str[len + add + i]) {
      return false;
    }
  }
  return true;
}
var partition = function (s) {
  let res = [];
  let dfs = (cur, start) => {
    // 当前已经到达了最后一个元素
    if (start >= s.length) {
      res.push(cur.slice());
      return;
    }
    for (let i = start; i < s.length; i++) {
      // 字符串切割
      let str = s.slice(start, i + 1);
      if (str && isPal(str) ) {
        cur.push(str);
        dfs(cur, i + 1);
        // 回溯
        cur.pop();
      }
    }
  }
  dfs([], 0);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 1190. 反转每对括号间的子串

仰望星空的人,不应该被嘲笑

题目描述

给出一个字符串 s(仅含有小写英文字母和括号)。

请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。

注意,您的结果中 不应 包含任何括号。

示例 1:

输入:s = "(abcd)"
输出:"dcba"

示例 2:

输入:s = "(u(love)i)"
输出:"iloveu"

示例 3:

输入:s = "(ed(et(oc))el)"
输出:"leetcode"

示例 4:

输入:s = "a(bcdefghijkl(mno)p)q"
输出:"apmnolkjihgfedcbq"

提示:

0 <= s.length <= 2000
s 中只有小写英文字母和括号
我们确保所有括号都是成对出现的

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

初始化栈,栈顶元素为 " "
遇到 '(': 向栈顶压入空字符串
遇到 ')': 把栈顶的最后一个元素翻转 + 栈顶倒数第二个元素
遇到 字符: 直接将栈顶最后一个元素与它拼上

参考 tuotuoli 大佬解题思路

样例栈数组操作示意:

样例:a(bcdefghijkl(mno)p)q

a ['a']
( ['a', '']
b ['a', 'b']
c ['a', 'bc']
d ['a', 'bcd']
e ['a', 'bcde']
f ['a', 'bcdef']
g ['a', 'bcdefg']
h ['a', 'bcdefgh']
i ['a', 'bcdefghi']
j ['a', 'bcdefghij']
k ['a', 'bcdefghijk']
l ['a', 'bcdefghijkl']
( ['a', 'bcdefghijkl', '']
m ['a', 'bcdefghijkl', 'm']
n ['a', 'bcdefghijkl', 'mn']
o ['a', 'bcdefghijkl', 'mno']
) ['a', 'bcdefghijklonm']
p ['a', 'bcdefghijklonmp']
) ['apmnolkjihgfedcb']
q ['apmnolkjihgfedcbq']
/**
 * @param {string} s
 * @return {string}
 */
var reverseParentheses = function(s) {
  let stack = ['']
  for(let i=0;i<s.length;i++){
    let ch = s[i]
    if(ch === '('){
      stack.push('')
    }else if(ch === ')'){
      let str = stack.pop()
      let tmp = str.split('').reverse().join('')
      stack[stack.length-1] += tmp
    }else{
      stack[stack.length-1] += ch
    }
  }
  return stack.pop()
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 212. 单词搜索 II

仰望星空的人,不应该被嘲笑

题目描述

给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。

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

示例:

输入: 
words = ["oath","pea","eat","rain"] and board =
[
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]

输出: ["eat","oath"]
说明:
你可以假设所有输入都由小写字母 a-z 组成。

提示:

你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯?
如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这个问题: 实现Trie(前缀树)。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-search-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

参考力扣官网分析:实现 Trie (前缀树)

  • 判断是否找到了,通过传递节点的END来判断

  • 判断是否重复访问,通过动态更改走过的网格点来判断,就不需要再定义一个vis数组了

参考大佬:秦时明月字典树建树解法(二)

var findWords = function(grid, words) {
  // 存放最终结果集
  let res = []
  // 字典树节点
  class TrieNode {
    constructor(){
      this.end = false
      this.child = {}
    }
  }
  // 最终形成的字典树根节点
  let root = null
  let Trie = function(){
    root = new TrieNode()
  }
  // 建立字典树
  Trie.prototype.insert = (word) => {
    let cur = root
    for(let i=0;i<word.length;i++){
      if(!cur.child[word[i]]){
        cur.child[word[i]] = new TrieNode()
      }
      cur = cur.child[word[i]]
    }
    cur.end = true
  }
  // 创建根节点
  let trie = new Trie()
  // 进行建树操作
  for(let i=0;i<words.length;i++){
    trie.insert(words[i])
  }
  let dfs = (x,y,t,cur) => {
    if(cur.end){
      res.push(t)
      cur.end = false // 避免重复计算
    }
    // 剪枝条件:1.边界处理 2.下一步是否可走 3.下一步字典树是否可走
    if(x<0 || x>=grid.length || y<0 || y>=grid[0].length || grid[x][y] == '#' || !cur.child[grid[x][y]]) return
    let tmp = grid[x][y]
    grid[x][y] = '#'  // 走
    cur = cur.child[tmp]
    dfs(x+1,y,t+tmp,cur)  // 上下左右四个方向遍历
    dfs(x,y+1,t+tmp,cur)
    dfs(x-1,y,t+tmp,cur)
    dfs(x,y-1,t+tmp,cur)
    grid[x][y] = tmp // 回溯(还原)
  }
  // 对单词表进行全局搜索
  for(let i=0;i<grid.length;i++){
    for(let j=0;j<grid[0].length;j++){
      dfs(i,j,'',root)
    }
  }
  return res
};

附上完整字典树(前缀树)模板,日后可用~

在 Trie 树中查找键

每个键在 trie 中表示为从根到内部节点或叶的路径。我们用第一个键字符从根开始,。检查当前节点中与键字符对应的链接。有两种情况:

  • 存在链接。我们移动到该链接后面路径中的下一个节点,并继续搜索下一个键字符。
  • 不存在链接。若已无键字符,且当前结点标记为 isEnd,则返回 true。否则有两种可能,均返回 false :
    还有键字符剩余,但无法跟随 Trie 树的键路径,找不到键。
    没有键字符剩余,但当前结点没有标记为 isEnd。也就是说,待查找键只是Trie树中另一个键的前缀。


查找 Trie 树中的键前缀

该方法与在 Trie 树中搜索键时使用的方法非常相似。我们从根遍历 Trie 树,直到键前缀中没有字符,或者无法用当前的键字符继续 Trie 中的路径。与上面提到的“搜索键”算法唯一的区别是,到达键前缀的末尾时,总是返回 true。我们不需要考虑当前 Trie 节点是否用 “isend” 标记,因为我们搜索的是键的前缀,而不是整个键。

作者:LeetCode
链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/shi-xian-trie-qian-zhui-shu-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

var findWords = function(grid, words) {
  // 存放最终结果集
  let res = []
  // 字典树节点
  class TrieNode {
    constructor(){
      this.end = false
      this.child = {}
    }
  }
  // 最终形成的字典树根节点
  let root = null
  let Trie = function(){
    root = new TrieNode()
  }
  // 建立字典树
  Trie.prototype.insert = (word) => {
    let cur = root
    for(let i=0;i<word.length;i++){
      if(!cur.child[word[i]]){
        cur.child[word[i]] = new TrieNode()
      }
      cur = cur.child[word[i]]
    }
    cur.end = true
  }
  // 在 Trie 树中查找键
  let searchPrefix = (word) => {
    let cur = root
    for(let i=0;i<word.length;i++){
      if(cur.child[word[i]]){
        cur = cur.child[word[i]]
      }else{
        return null
      }
    }
    return cur
  }
  Trie.prototype.search = (word) => {
    let cur = searchPrefix(word)
    return cur !== null && cur.end
  }
  // 查找 Trie 树中的键前缀
  Trie.prototype.startsWith = (pre) => {
    return searchPrefix(pre) != null
  }
  // 创建根节点
  let trie = new Trie()
  // 进行建树操作
  for(let i=0;i<words.length;i++){
    trie.insert(words[i])
  }
  let dfs = (x,y,t,cur) => {
    if(cur.end){
      res.push(t)
      cur.end = false // 避免重复计算
    }
    // 剪枝条件:1.边界处理 2.下一步是否可走 3.下一步字典树是否可走
    if(x<0 || x>=grid.length || y<0 || y>=grid[0].length || grid[x][y] == '#' || !cur.child[grid[x][y]]) return
    let tmp = grid[x][y]
    grid[x][y] = '#'  // 走
    cur = cur.child[tmp]
    dfs(x+1,y,t+tmp,cur)  // 上下左右四个方向遍历
    dfs(x,y+1,t+tmp,cur)
    dfs(x-1,y,t+tmp,cur)
    dfs(x,y-1,t+tmp,cur)
    grid[x][y] = tmp // 回溯(还原)
  }
  // 对单词表进行全局搜索
  for(let i=0;i<grid.length;i++){
    for(let j=0;j<grid[0].length;j++){
      dfs(i,j,'',root)
    }
  }
  return res
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 17. 电话号码的字母组合

仰望星空的人,不应该被嘲笑

题目描述

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

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

示例:

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

说明:

尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

解题思路

采用回溯做法,对于当前选项,我们可以重复选择,所以 for 循环那里从 0 开始,对于字母组合我们做一个 map映射即可。

参考 xiao_ben_zhu 大佬的图解

var letterCombinations = function (digits) {
  if(!digits.length) return [];
  // 直接映射
  const map = { '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz' };
  let res = [];
  let dfs = (cur, start) => {
    if (start >= digits.length) {
      res.push(cur);
      return;
    }
    // 取当前可选的字母组合
    let str = map[digits[start]];
    for (let i = 0; i < str.length; i++) {
      dfs(cur + str[i], start + 1);
    }
  }
  dfs('', 0);
  return res;
};

解法2

这个是没用回溯之前写的一份代码,简单来说就是利用了层次遍历的特性,反正每次取字母都是可以重复的,直接遍历即可,然后进队列。

var letterCombinations = function(digits) {
  if(!digits.length) return []
  const map = { '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz' };
  let queue = []
  queue.push('')
  for(let i=0;i<digits.length;i++){
      let size = queue.length
      while(size--){
          let cur = queue.shift()
          let str = map[digits[i]]
          for(let j=0;j<str.length;j++){
              queue.push(cur+str[j])
          }
      }
  }
  return queue
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 946. 验证栈序列

仰望星空的人,不应该被嘲笑

题目描述

给定 pushedpopped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false

示例 1:

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

示例 2:

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。

提示:

0 <= pushed.length == popped.length <= 1000
0 <= pushed[i], popped[i] < 1000
pushed  popped 的排列。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/validate-stack-sequences
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

借助一个新栈来存放入栈的元素,然后每次和出栈的元素进行比对,如果匹配成功,双方进行出栈操作,最后,如果这个新栈为空,那么代表这个栈入栈和出栈序列是合理的,返回 true,否则返回false

/**
 * @param {number[]} pushed
 * @param {number[]} popped
 * @return {boolean}
 */
var validateStackSequences = function(pushed, popped) {
    // 借助一个新的栈
    let stack = []
    for(let cur of pushed){
        // 存放入栈的元素
        stack.push(cur)
        // 和出栈元素进行比对,如果匹配都弹出栈
        while(stack[stack.length-1] === popped[0] && stack.length){
            stack.pop()
            popped.shift()
        }
    }
    return !stack.length
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 78. 子集

仰望星空的人,不应该被嘲笑

题目描述

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

一道组合相关的题目,采用回溯来做即可,题目说明不包含重复元素,于是我们也无需排序然后判断相邻元素是否相等来去重了。

var subsets = function(nums) {
  let res = [];
  let dfs = (t,start) => {
    res.push(t);
    for(let i=start;i<nums.length;i++){
      t.push(nums[i]);
      dfs(t.slice(),i+1);
      t.pop();
    }
  }
  dfs([],0);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 933. 最近的请求次数

仰望星空的人,不应该被嘲笑

题目描述

写一个 RecentCounter 类来计算最近的请求。

它只有一个方法:ping(int t),其中 t 代表以毫秒为单位的某个时间。

返回从 3000 毫秒前到现在的 ping 数。

任何处于[t - 3000, t]时间范围之内的 ping 都将会被计算在内,包括当前(指 t 时刻)的 ping

保证每次对 ping 的调用都使用比之前更大的 t 值。

示例:

输入:inputs = ["RecentCounter","ping","ping","ping","ping"], inputs = [[],[1],[100],[3001],[3002]]
输出:[null,1,2,3,3]

提示:

每个测试用例最多调用10000次 ping。
每个测试用例会使用严格递增的 t 值来调用 ping
每次调用 ping 都有 1 <= t <= 10^9

题解

根据样例,发现越早发出的请求越早不在 3000ms 内的请求里

满足先进先出,考虑用队列

那么就将新请求加入队列,3000ms前发出的请求就出队列

队列的长度即为最近请求次数。

var RecentCounter = function() {
    this.queue = []
};

/** 
 * @param {number} t
 * @return {number}
 */
RecentCounter.prototype.ping = function(t) {
  // 将新请求加入队列
  this.queue.push(t)
  // 3000ms 前发出的请求就出队列
  while(this.queue[0] < t-3000){
    this.queue.shift()
  }
  return this.queue.length
};

/**
 * Your RecentCounter object will be instantiated and called as such:
 * var obj = new RecentCounter()
 * var param_1 = obj.ping(t)
 */

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 110. 平衡二叉树

仰望星空的人,不应该被嘲笑

题目描述

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

示例 1:

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

    3
   / \
  9  20
    /  \
   15   7
返回 true 

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4
返回 false 

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/balanced-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

dfs,平衡二叉树就是每个节点 的左右两个子树的高度差的绝对值不超过1。那么,我们可以自底向上,即采用后序遍历的方式,只要左右高度超过1了,直接设置 flase即可。

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isBalanced = function (root) {
    if(!root) return true;
    let res = true;
    let dfs = (root) => {
        // 先遍历左子树,在遍历右子树
        let left = root.left && dfs(root.left) + 1;
        let right = root.right && dfs(root.right) + 1;
        // 判断是否是平衡二叉树,就看高度差是否大于1
        if (Math.abs(left - right) > 1) res = false;
        // 返回左右子树深度的最大值
        return Math.max(left, right);
    }
    dfs(root);
    return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 101. 对称二叉树

仰望星空的人,不应该被嘲笑

题目描述

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3
 

进阶:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/symmetric-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

dfs,逐层进行比较,即自顶向底找,注意判断几个条件:

  • 如果左右节点都为空,可以
  • 如果左右节点一个为空,不可以
  • 如果左右节点值不相等,不可以
  • 最后递归左右子树镜像


参考 王尼玛 大佬图解

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function (root) {
    if (!root) return true;
    let dfs = (left, right) => {
        // 如果左右节点都为空,可以
        if (left == null && right == null) return true;
        // 如果左右节点一个为空,不可以
        if(left == null || right == null) return false;
        // 如果左右节点值不相等,不可以
        if (left.val !== right.val) return false;
        // 递归左右子树镜像
        return dfs(left.left, right.right) && dfs(left.right, right.left);
    }
    return dfs(root.left, root.right);
};

解法二

通过队列逐步一层一层来比较,只要出现不对称的情况,直接返回 false

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function (root) {
    if (!root) return true
    let queue = [root.left, root.right]
    while (queue.length) {
        let node1 = queue.shift()
        let node2 = queue.shift()
        if (!node1 && !node2) continue
        if (!node1 || !node2 || node1.val !== node2.val) return false
        queue.push(node1.left)
        queue.push(node2.right)
        queue.push(node1.right)
        queue.push(node2.left)
    }
    return true
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 980. 不同路径 III

仰望星空的人,不应该被嘲笑

题目描述

在二维网格 grid 上,有 4 种类型的方格:

1 表示起始方格。且只有一个起始方格。
2 表示结束方格,且只有一个结束方格。
0 表示我们可以走过的空方格。
-1 表示我们无法跨越的障碍。
返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。

每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。

示例 1:

输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
输出:2
解释:我们有以下两条路径:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)

示例 2:

输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]]
输出:4
解释:我们有以下四条路径: 
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)

示例 3:

输入:[[0,1],[2,0]]
输出:0
解释:
没有一条路能完全穿过每一个空的方格一次。
请注意,起始和结束方格可以位于网格中的任意位置。

提示:

1 <= grid.length * grid[0].length <= 20

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-paths-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

回溯算法,不过这道题需要我们走完所有空格,所以我们起初遍历的时候需要统计一下空格的数目,然后还有一个注意点就是重点也算是可走的路径的一个点,也需要统计进去,所以代码 cnt 值 初始化为 1

接下来就是回溯过程了,写了一个 check 函数,进行简单判断剪枝,然后就是往四个方向搜,每走一个格子就将当前格子设置为障碍(即 -1),然后搜索完后,回溯时,需要将障碍重设置为空格。

/**
 * @param {number[][]} grid
 * @return {number}
 */
var uniquePathsIII = function(grid) {
    let cnt = 1 // 统计地图中可走的方格个数,包括终点,故初始值为1
    let sx,sy // 记录起点坐标
    for(let i=0;i<grid.length;i++){
        for(let j=0;j<grid[0].length;j++){
            if(grid[i][j] === 1){
                sx = i
                sy = j
            }
            else if(grid[i][j] === 0){
                cnt++
            }
        }
    }
    return dfs(sx,sy,cnt,grid)
};
// 剪枝条件
let check = (sx,sy,grid) => {
    if(sx<0 || sx>=grid.length || sy<0 || sy>=grid[0].length || grid[sx][sy] == -1) return false
    return true
}

let dfs = (sx,sy,cnt,grid) => {
    if(!check(sx,sy,grid)) return 0
    if(grid[sx][sy] === 2){ // 走到终点时,也要判断一下当前所有空格是否走完
        return cnt === 0 ? 1:0
    }
    let res = 0
    grid[sx][sy] = -1  //走过的空格进行标记,设置为障碍即可
    res += dfs(sx+1,sy,cnt-1,grid)  // 四个方向进行搜索
    res += dfs(sx,sy+1,cnt-1,grid)
    res += dfs(sx-1,sy,cnt-1,grid)
    res += dfs(sx,sy-1,cnt-1,grid)
    grid[sx][sy] = 0  // 回溯过程,不影响后续dfs
    return res
}

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 20. 有效的括号

仰望星空的人,不应该被嘲笑

题目描述

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

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:

输入: "()"
输出: true

示例 2:

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

示例 3:

输入: "(]"
输出: false

示例 4:

输入: "([)]"
输出: false

示例 5:

输入: "{[]}"
输出: true

题解

发现越靠后的左括号,最先匹配,也就是后进先出的**,于是考虑使用栈这个数据结构。

/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function(s) {
  // 如果是奇数,不可能匹配成功,直接返回false
  if(s.length & 1) return false
  let stack = []
  for(let i=0;i<s.length;i++){
    if(s[i] === '(' || s[i] === '{' || s[i] === '[') stack.push(s[i])
    else if(s[i] === ')' && stack[stack.length-1] === '(') stack.pop()
    else if(s[i] === '}' && stack[stack.length-1] === '{') stack.pop()
    else if(s[i] === ']' && stack[stack.length-1] === '[') stack.pop()
    else return false
  }
  return !stack.length
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 22. 括号生成

仰望星空的人,不应该被嘲笑

题目描述

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

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/generate-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

这道题,看了大佬的题解,发现真是有意思,现在来解释一下。

我们可以直接走可行的情况,对于不可行的情况,自然就剪掉了。

关键在于左右括号如何选择,首先,对于左括号,起初我们必然是要选的,然后我们也可以全部选完,因此,只要有左括号我们必须选,而对于右括号而言,它的剩余数量必须大于剩余左括号数量,我们才能选右括号。

举个反例,假如我们现在已经有了 (())n = 3,然后左右括号都还剩一个,如果理解选 ),岂不是就 (()))了,显示不是有效的括号,应该被剪掉才是,因此,我们必须严格右括号剩余数量必须大于剩余左括号数量,我们才能选右括号。

参考 笨猪爆破组 大佬图解

var generateParenthesis = function (n) {
  let res = [];
  let dfs = (cur, left, right) => {
    if (cur.length === 2 * n) {
      res.push(cur);
      return;
    }
    // 左括号还存在,就可以选左括号
    if (left > 0) dfs(cur + '(', left - 1, right);
    // 右括号数量要大于左括号,才可以选右括号
    if (right > left) dfs(cur + ')', left, right - 1);
  }
  dfs('', n, n);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 51. N 皇后

仰望星空的人,不应该被嘲笑

题目描述

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

上图为 8 皇后问题的一种解法。

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例:

输入:4
输出:[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。

提示:

皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

对于 n 皇后问题,经典的回溯算法,我们采用一行放一个,然后逐行来放,这样我们就不用在剪枝的时候判断是否同行了。只需要判断是否同列 或者 同一斜线就好了。

参考xiao_ben_zhu大佬图解

var solveNQueens = function(n) {
  let res = [];
  let grid = new Array(n); // 初始化一个地图
  for(let i=0;i<n;i++){
    grid[i] = new Array(n).fill('.');
  }
  // 剪枝条件 
  let check = (x,y)=>{
    for(let i=0;i<x;i++){
      for(let j=0;j<n;j++){
        // 判断同列 或者 同一斜线即可(不需要判断同行是因为一行一行放的,一定不同行)
        if(grid[i][j] == 'Q' && (j == y || i+j == x+y || i-j == x-y) ){
          return true;
        }
      }
    }
    return false;
  }
  let dfs = (t) => {
    if(t === n ){
      let ans = grid.slice(); // 拷贝一份,对输出做处理
      for(let i=0;i<n;i++){
        ans[i] = ans[i].join('');
      }
      res.push(ans);
      return;
    }
    for(let i=0;i<n;i++){
      if(check(t,i)) continue;
      grid[t][i] = 'Q';
      dfs(t+1);
      grid[t][i] = '.';
    }
  }
  dfs(0);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 111. 二叉树的最小深度

仰望星空的人,不应该被嘲笑

题目描述

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

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

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

示例:

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

    3
   / \
  9  20
    /  \
   15   7

返回它的最小深度 2.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

dfs,对于当前节点,判断一下是否有左右孩子,有的话就取左右孩子得到的最小深度,如果只有左孩子,递归左孩子,只有有孩子,则递归右孩子。到子节点时,直接返回 1

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var minDepth = function(root) {
    let dfs = (root) => {
        if(root == null) return 0;
        // 左右孩子都有,取左右孩子的最小值
        if(root.left && root.right){
            return 1 + Math.min(dfs(root.left),dfs(root.right));
        }else if(root.left){
            return 1 + dfs(root.left);
        }else if(root.right){
            return 1 + dfs(root.right);
        }
        return 1;
    }
    return dfs(root);
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 1219. 黄金矿工

仰望星空的人,不应该被嘲笑

题目描述

你要开发一座金矿,地质勘测学家已经探明了这座金矿中的资源分布,并用大小为 m * n 的网格 grid 进行了标注。每个单元格中的整数就表示这一单元格中的黄金数量;如果该单元格是空的,那么就是 0

为了使收益最大化,矿工需要按以下规则来开采黄金:

每当矿工进入一个单元,就会收集该单元格中的所有黄金。
矿工每次可以从当前位置向上下左右四个方向走。
每个单元格只能被开采(进入)一次。
不得开采(进入)黄金数目为 0 的单元格。
矿工可以从网格中 任意一个 有黄金的单元格出发或者是停止。

示例 1:

输入:grid = [[0,6,0],[5,8,7],[0,9,0]]
输出:24
解释:
[[0,6,0],
 [5,8,7],
 [0,9,0]]
一种收集最多黄金的路线是:9 -> 8 -> 7

示例 2:

输入:grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]]
输出:28
解释:
[[1,0,7],
 [2,0,6],
 [3,4,5],
 [0,3,0],
 [9,0,20]]
一种收集最多黄金的路线是:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7

提示:

1 <= grid.length, grid[i].length <= 15
0 <= grid[i][j] <= 100
最多 25 个单元格中有黄金。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-with-maximum-gold
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

这题也是搜索相关,四个方向,不允许重复,不过这次我们需要从不同起点搜索,而且为了减少搜索次数,我们得从黄金数量不为0的点开始搜。然后每当走不下去的时候,就比较一下当前黄金数量,求出最大值即可。

/**
 * @param {number[][]} grid
 * @return {number}
 */
var getMaximumGold = function(grid) {
    if(!grid || !grid.length) return 0
    let vis = []
    // 最终收集的最多黄金数量
    let maxGold = 0
    for(let i=0;i<grid.length;i++) vis[i] = []
    // 剪枝条件
    let check = (x,y) => {
        if(x<0 || x>=grid.length || y<0 || y>=grid[0].length || vis[x][y] === 1 || !grid[x][y]) return false
        return true
    }
    let dfs = (x,y,total) => {
        if(check(x,y)){
            vis[x][y] = 1 //防止重复
            dfs(x+1,y,total+grid[x][y]) // 四个方向搜索
            dfs(x,y+1,total+grid[x][y])
            dfs(x-1,y,total+grid[x][y])
            dfs(x,y-1,total+grid[x][y])
            vis[x][y] = 0
        }else{
            // 走到底了,就比较一下当前黄金数量
            maxGold = Math.max(maxGold,total)
        }
    }
    // 起点从非0单元格开始
    for(let i=0;i<grid.length;i++){
        for(let j=0;j<grid[0].length;j++){
            if(grid[i][j]){
                dfs(i,j,0)
            }
        }
    }
    return maxGold
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 437. 路径总和 III

仰望星空的人,不应该被嘲笑

题目描述

给定一个二叉树,它的每个结点都存放着一个整数值。

找出路径和等于给定数值的路径总数。

路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。

示例:

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1

返回 3。和等于 8 的路径有:

1.  5 -> 3
2.  5 -> 2 -> 1
3.  -3 -> 11

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-sum-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

本题采用方式就是先序遍历,对于遍历到的每个节点,我们都进行一次 dfs,但是考虑本题的数字范围为负数,对于当前一条路我们得到了一条路径后,假如后面还有路可以走,那么我们还是继续走,因为后面可能出现正负抵消的情况。

面试中如果遇到题例没有明确说明数字范围,建议和面试官沟通。

/**
 * 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
 * @param {number} sum
 * @return {number}
 */
var pathSum = function (root, sum) {
    // 定义一个计时器
    let cnt = 0;
    // 先序遍历所有根节点
    let preOrder = (root, sum) => {
        if (root == null) return;
        dfs(root, sum);
        preOrder(root.left, sum);
        preOrder(root.right, sum);
    }
    let dfs = (root, sum) => {
        if (root == null) return;
        sum -= root.val;
        // 求和满足,累加
        if (sum === 0) cnt++;
        // 递归左右子树,如果当前和为0了,但是下面还是有路,还是继续走下去
        // 因为本题数值范围存在负数,可能继续走下去还存在满足条件的路径
        dfs(root.left, sum);
        dfs(root.right, sum);
    }
    preOrder(root, sum);
    return cnt;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 921. 使括号有效的最少添加

仰望星空的人,不应该被嘲笑

题目描述

给定一个由 '(' ')' 括号组成的字符串 S,我们需要添加最少的括号( '(' 或是 ')',可以在任何位置),以使得到的括号字符串有效。

从形式上讲,只有满足下面几点之一,括号字符串才是有效的:

它是一个空字符串,或者
它可以被写成 AB (A 与 B 连接), 其中 A 和 B 都是有效字符串,或者
它可以被写作 (A),其中 A 是有效字符串。
给定一个括号字符串,返回为使结果字符串有效而必须添加的最少括号数。

示例 1:

输入:"())"
输出:1

示例 2:

输入:"((("
输出:3

示例 3:

输入:"()"
输出:0

示例 4:

输入:"()))(("
输出:4

提示:

S.length <= 1000
S 只包含 '(' 和 ')' 字符。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-add-to-make-parentheses-valid
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

借助一个新栈,然后遍历当前字符串,如果当前栈顶元素和目前字符括号匹配,则弹出栈顶元素,否则进行入栈操作,最后需要的括号数即为栈剩余的元素个数

/**
 * @param {string} S
 * @return {number}
 */
var minAddToMakeValid = function(S) {
    // 长度为0,无须添加
    if(!S.length) return 0
    let stack = []
    for(let i=0;i<S.length;i++){
        let ch = S[i]
        // 如果当前栈顶元素和目前字符括号匹配,则弹出栈顶元素
        if(ch === ')' && stack[stack.length-1] === '(') stack.pop()
        else stack.push(ch)
    }
    // 栈的剩余元素个数,即需要的括号数
    return stack.length
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 401. 二进制手表

仰望星空的人,不应该被嘲笑

题目描述

二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)

每个 LED 代表一个 0 或 1,最低位在右侧。

例如,上面的二进制手表读取 “3:25”。

给定一个非负整数 n 代表当前 LED 亮着的数量,返回所有可能的时间。

示例:

输入: n = 1
返回: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", "0:08", "0:16", "0:32"]
 

提示:

输出的顺序没有要求。
小时不会以零开头,比如 “01:00 是不允许的,应为 “1:00”。
分钟必须由两位数组成,可能会以零开头,比如 “10:2 是无效的,应为 “10:02”。
超过表示范围(小时 0-11,分钟 0-59)的数据将会被舍弃,也就是说不会出现 "13:00", "0:61" 等时间。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-watch
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

回溯算法,我的解法类似于全排列做法,将10个小灯泡进行排列组合,然后根据 01 来判断灯泡是否亮,如果亮了,加上对应二进制,然后将 0-3分给小时来计算,将 4-9分给分钟来计算,但是要考虑一下,就是可能会出现重复情况,于是用 Set数据结构维护一下就好了。

var readBinaryWatch = function(num) {
    let res = new Set();
    let vis = new Array(10).fill(0)
    let check = (hour,minutes) => {
      if(hour>=0 && hour<=11 && minutes>=0 && minutes<=59) return true;
      return false;
    }
    let dfs = (t,vis) => {
      if(t==0){
        let hour = vis[0]*1 + vis[1]*2 + vis[2]*4 + vis[3]*8;
        let minutes = vis[4]*1 + vis[5]*2 + vis[6]*4 + vis[7]*8 + vis[8]*16 + vis[9]*32;
        if(check(hour,minutes)){
          let tmp = `${hour}:${minutes >= 10? minutes: '0'+minutes}`;
          res.add(tmp);
        }
      }
      for(let i=0;i<10;i++){
        if(vis[i]) continue;
        vis[i] = 1;
        dfs(t-1,vis);
        vis[i] = 0;
      }
    }
    dfs(num,vis);
    return [...res];
};

补充,后面看到有大佬这样做,进行了去重操作,关键点在回溯 for循环那里。其实这个相当于全排列了。

var readBinaryWatch = function(num) {
    let res = [];
    let vis = new Array(10).fill(0)
    let check = (hour,minutes) => {
      if(hour>=0 && hour<=11 && minutes>=0 && minutes<=59) return true;
      return false;
    }
    let dfs = (t,cnt,vis) => {
      if(t==0){
        let hour = vis[0]*1 + vis[1]*2 + vis[2]*4 + vis[3]*8;
        let minutes = vis[4]*1 + vis[5]*2 + vis[6]*4 + vis[7]*8 + vis[8]*16 + vis[9]*32;
        if(check(hour,minutes)){
          let tmp = `${hour}:${minutes >= 10? minutes: '0'+minutes}`;
          res.push(tmp);
        }
        return;
      }
      for(let i=cnt;i<=10-t;i++){
        if(vis[i]) continue;
        vis[i] = 1;
        dfs(t-1,i+1,vis);
        vis[i] = 0;
      }
    }
    dfs(num,0,vis);
    return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 144. 二叉树的前序遍历

仰望星空的人,不应该被嘲笑

题目描述

给定一个二叉树,返回它的 前序 遍历。

示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [1,2,3]

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

递归解法

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var preorderTraversal = function (root) {
    if (!root) return [];
    let res = [];
    let fun = (root) => {
        if (root) res.push(root.val);
        root.left && fun(root.left);
        root.right && fun(root.right);
    }
    fun(root);
    return res;
};

迭代做法

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var preorderTraversal = function (root) {
    if (!root) return [];
    let res = [];
    let queue = [root];
    while (queue.length) {
        let size = queue.length;
        while (size--) {
            // 取左孩子
            let node = queue.pop();
            res.push(node.val);
            // 优先放右孩子
            node.right && queue.push(node.right);
            node.left && queue.push(node.left);
        }
    }
    return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 404. 左叶子之和

仰望星空的人,不应该被嘲笑

题目描述

计算给定二叉树的所有左叶子之和。

示例:

    3
   / \
  9  20
    /  \
   15   7

在这个二叉树中,有两个左叶子,分别是 9  15,所以返回 24

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-of-left-leaves
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

dfs,求左叶子之和,叶子结点我们比较好判断,而对于左孩子,我们设置一个标记就好了,例如左孩子标记 1,右孩子标记 0,那么当且仅当是叶子节点,并且标记为 1(即左孩子)时,我们进行累加求和。

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var sumOfLeftLeaves = function (root) {
    if (!root) return 0;
    let res = 0;
    let dfs = (cur,root) => {
        // 判断是叶子节点并且是左子树
        if (!root.left && !root.right && cur) {
            res += root.val;
            return;
        }
        // 先遍历左子树在遍历右子树
        // 设置cur为1代表是左子树,为0代表右子树
        root.left && dfs(1,root.left);
        root.right && dfs(0,root.right);
    }
    dfs(0,root);
    return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 907. 子数组的最小值之和

仰望星空的人,不应该被嘲笑

题目描述

给定一个整数数组 A,找到 min(B) 的总和,其中 B 的范围为 A 的每个(连续)子数组。

由于答案可能很大,因此返回答案模 10^9 + 7

示例:

输入:[3,1,2,4]
输出:17
解释:
子数组为 [3][1][2][4][3,1][1,2][2,4][3,1,2][1,2,4][3,1,2,4] 
最小值为 3,1,2,4,1,1,2,1,1,1,和为 17

提示:

1 <= A <= 30000
1 <= A[i] <= 30000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-of-subarray-minimums
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

搬运 jack-108大佬的题解

既然是求子数组中最小值的和,就是求 以 A[i] 作为最小数能构成的数组有多少个。

比如 [2,4,1,2] ,以1 为最小数。能构成的数组数为 (2+1)*(1+1)2 表示 1 左面有两个比它大的数,1 表示 1 右面有一个比它大的数。

用单调栈求出 A[i] 对应的左右最近比 A[i] 小的数,记索引为 prev,next,A[i] 为最小数能形成的数组为

(i-prev[i])*(next[i]-i)

这里为什么没有加 1 了呢,因为 prev[i]已经是比 A[i] 小的数了,能和 A[i] 形成数组的都是比 A[i] 大的数。

我的解题方式:

注释已经足够详细,还是补充一下,我参考了大佬的解题代码,只不过我是直接求出来了以当前 A[i] 为最小值的子数组左右两边 大于或等于当前值的个数。这样后续求和直接相乘即可。(不过这里要强调一下,如果左边设置大于等于了,右边就只能是大于了,不然会重复计算相等的值)

开始有点看不懂大佬为啥左边初始化为 -1,右边初始化为 A.length 。假如我们遇到了这种情况,左边都比当前 A[i] 要大,那我们维护的单调递减栈就会不断出栈,不断出栈,直到栈为空为止,此时左边个数应该为 i+1(从 0 开始计数嘛),那么这部分大佬设为 -1 就很巧妙了,问题也就自然明白啦,个人感觉自己写的还是比较好理解一点,不然直接弄一个 -1 ,第一次用单调栈,还是不太熟...

那么对于右边初始化为 A.length ,也是同理啦,当右边都比当前 A[i] 要大,那我们维护的单调递减栈就会不断出栈,不断出栈,直到栈为空为止,此时右边个数应该为 A.length-i(不用+1的原因是从0计数),那么这部分大佬设为 A.length 就很巧妙了,依旧清晰明了。

/**
 * @param {number[]} A
 * @return {number}
 */
var sumSubarrayMins = function(A) {
  let mod = 1e9+7
  // 维护一个栈
  let stack = []
  // 求以A[i]为最小值的子数组左边大于或等于自己的个数
  let prev = []
  for(let i=0;i<A.length;i++){
    while(stack.length && A[stack[stack.length-1]] >= A[i]) stack.pop()
    // 如果栈为空,即左边都比自己大,则返回i+1,否则返回i-栈顶元素(即保存的下标值)
    prev[i] = stack.length ? i - stack[stack.length-1] : i+1
    stack.push(i)
  }
  stack = []
  // 求以A[i]为最小值的子数组右边大于自己的个数(没有等号是因为不会重复计算相等的值)
  let nextv = []
  for(let i=A.length-1;i>=0;i--){
    while(stack.length && A[stack[stack.length-1]] > A[i]) stack.pop()
    // 如果栈为空,即右边都比自己大,则返回A.length-i,否则返回栈顶元素(即保存的下标值)-i
    nextv[i] = stack.length? stack[stack.length-1] - i : A.length-i
    stack.push(i)
  }
  let sum = 0
  for(let i=0;i<A.length;i++){
    // 以A[i] 为最小值的子数组的组合共有prev[i]*nextv[i]种情况,那么和的话乘以A[i]累加即可
    sum += (prev[i]*nextv[i]*A[i])
    // 按题意,进行取模运算
    sum %= mod
  }
  return sum
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 59. 螺旋矩阵 II

仰望星空的人,不应该被嘲笑

题目描述

给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/spiral-matrix-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

按照螺旋矩阵模拟即可,先从左到右,在从上到下,再从右到左,再从下到上。

每次进行cur++操作,直到累加到total为止。最后返回二维数组即可(没想到 js二维数组也是这样方便...)

/**
 * @param {number} n
 * @return {number[][]}
 */
var generateMatrix = function(n) {
    let top = 0, bottom =n-1
    let left = 0, right = n-1
    let res = []
    for(let i=0;i<n;i++) res[i] = []
    let cur = 1, total = n*n
    while(cur<=total){
        for(let i=left;i<=right;i++) res[top][i] = cur++  // 从左到右
        top++
        for(let i=top;i<=bottom;i++) res[i][right] = cur++ // 从上到下
        right--
        for(let i=right;i>=left;i--) res[bottom][i] = cur++ // 从右到左
        bottom--
        for(let i=bottom;i>=top;i--) res[i][left] = cur++ // 从下到上
        left++
    }
    return res
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 面试题 16.11. 跳水板

仰望星空的人,不应该被嘲笑

题目描述

你正在使用一堆木板建造跳水板。有两种类型的木板,其中长度较短的木板长度为shorter,长度较长的木板长度为longer。你必须正好使用k块木板。编写一个方法,生成跳水板所有可能的长度。

返回的长度需要从小到大排列。

示例 1

输入:
shorter = 1
longer = 2

k = 3
输出: [3,4,5,6]
解释:
可以使用 3  shorter,得到结果 3;使用 2  shorter  1  longer,得到结果 4 。以此类推,得到最终结果。

提示:

0 < shorter <= longer
0 <= k <= 100000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/diving-board-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

排列组合也算比较简单,需要 k 个板子,当我们短板有 i 个的时候,长板子就是 k-i 个,由于题目要求是将结果从小到大进行排序,那么我们起初就尽可能多的取短板子,最后结果就是通过 [0,k] 范围内遍历一遍即可。

对于特殊情况,即短板和长板长度相同时,我们只需要返回 k*len 即可,不然会重复计算。

var divingBoard = function(shorter, longer, k) {
    if(k===0) return []
    if(shorter === longer) return [k*shorter]
    let res = []
    for(let i=k;i>=0;i--){
        let shortCnt = i
        let longCnt = k-i
        let cnt = shortCnt*shorter + longCnt*longer
        res.push(cnt)
    }
    return res
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 1249. 移除无效的括号

仰望星空的人,不应该被嘲笑

题目描述

给你一个由 '('')' 和小写字母组成的字符串 s

你需要从字符串中删除最少数目的 '(' 或者 ')' (可以删除任意位置的括号),使得剩下的「括号字符串」有效。

请返回任意一个合法字符串。

有效「括号字符串」应当符合以下 任意一条 要求:

空字符串或只包含小写字母的字符串
可以被写作 AB(A 连接 B)的字符串,其中 AB 都是有效「括号字符串」
可以被写作 (A) 的字符串,其中 A 是一个有效的「括号字符串」

示例 1:

输入:s = "lee(t(c)o)de)"
输出:"lee(t(c)o)de"
解释:"lee(t(co)de)" , "lee(t(c)ode)" 也是一个可行答案。

示例 2:

输入:s = "a)b(c)d"
输出:"ab(c)d"

示例 3:

输入:s = "))(("
输出:""
解释:空字符串也是有效的

示例 4:

输入:s = "(a(b(c)d)"
输出:"a(b(c)d)"

提示:

1 <= s.length <= 10^5
s[i] 可能是 '('、')' 或英文小写字母

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-remove-to-make-valid-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

一开始我是想着只要对应括号匹配就好了,将多余的右括号删掉,但是这个样例 ))(( 不可能过的,因为左括号也可以不匹配呀。于是我想着将括号对应字符串索引存起来,起初我们可以将不匹配的右括号还是按原来方法删掉就好了,匹配一个就删掉一个对应左括号的索引值,最后多出来的索引值全删掉就好了,这样就不会出现左括号还余留的情况。

这里提示一下:不要用 splice去删除指定下标的元素,splice会改变原数组长度,而你原本存的下标是基于原数组的。
delete方法不会改变数组长度,但删除的那个位置会变成'undefined',所以我们用fliter方法过滤一遍出有效值 arr=arr.filter(item=>item)

最后通过 res.join('') 方法,将数组转换成我们最后要的字符串即可。

var minRemoveToMakeValid = function(s) {
    let res = [...s]
    let stack = []
    for(let i=-0;i<s.length;i++){
        let ch = s[i]
        if(ch === '('){
            stack.push(i)
        }else if(ch === ')'){
            if(stack.length) stack.pop()
            else delete(res[i])
        }
    }
    while(stack.length){
        let idx = stack.pop()
        delete(res[idx])
    }
    res = res.filter(item=>item)
    return res.join('')
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 739. 每日温度

仰望星空的人,不应该被嘲笑

题目描述

请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/daily-temperatures
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

本题用到了单调栈的思路,将原本需要 O(n^2) 的时间复杂度降低到了 O(n)。

我们只需要维护一个新栈,首先遍历整个数组,只要栈不为空,如果当前的数字大于栈顶元素,则必定是第一个大于它的元素,我们只需要求出相差距离,然后存入结果就好了。

维护的新栈存放的是我们的元素下标,这样我们求距离时就很方便了,本题我觉得可以说是单调栈的一个模板题。专栏后续会有单调栈其它题目,可以查阅哈。

/**
 * @param {number[]} T
 * @return {number[]}
 */
var dailyTemperatures = function(T) {
    let stack = []
    // 初始化气温列表,默认值为0
    let res = new Array(T.length).fill(0)
    for(let i=0;i<T.length;i++){
        //将栈顶元素下标对应的值和当前元素进行比较
        while(T[i] > T[stack[stack.length-1]] && stack.length){
            let idx = stack.pop()
            res[idx] = i-idx
        }
        stack.push(i)
    }
    return res
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 90. 子集 II

仰望星空的人,不应该被嘲笑

题目描述

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

本题还是挺有意思的,我们要求的是子集,但是子集要进行去重操作,采用的做法是先对原数组进行排序,那么排序后的数组重复的元素必定是相邻的,然后在遍历解空间树的时候,要做一个去重的操作,当遇到重复出现,也就是和前面相邻元素相同的时候,直接跳过该节点,不让它向下递归。具体示意图如下:


参考大佬题解

dfs的话,一条路会一直走下去,然后回溯回来,在走之前,start是当前层第一个元素,只有当前元素下标大于 start才会有重复元素,而对于不同层的重复元素,我们不应该切断,应该继续走,不然就不会有 [1,2,2]这样的子集出现了。

var subsetsWithDup = function(nums) {
  let res = [];
  nums.sort((a,b)=>a-b);
  let dfs = (t,start) => {
    res.push(t);
    for(let i=start;i<nums.length;i++){
      // 同层重复,跳过
      if(i>start && nums[i-1] == nums[i]) continue;
      t.push(nums[i]);
      dfs(t.slice(),i+1);
      t.pop();
    }
  }
  dfs([],0);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 46. 全排列

仰望星空的人,不应该被嘲笑

题目描述

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

序列不重复就很简单了,维护一个 vis数组,不重复取就好了。

var permute = function (nums) {
  let res = [];
  let vis = {};
  let dfs = (t) => {
    if (t.length == nums.length) {
      res.push(t);
    }
    for (let i = 0; i < nums.length; i++) {
      if (vis[i]) continue;
      vis[i] = true;
      t.push(nums[i]);
      dfs(t.slice());
      t.pop();
      vis[i] = false;
    }
  }
  dfs([]);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 40. 组合总和 II

仰望星空的人,不应该被嘲笑

题目描述

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为target的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
  [1,2,2],
  [5]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

这道题也是一道组合题,但是这道题数组里面是存在重复元素的,组合题的话,为了更好地去重,我们可以先对数组进行排序,然后对于每一层如果相邻元素相同,就剪掉该分支即可。

参考xiao_ben_zhu大佬图解

注意求和那里,如果只判断是否相等的话,可能会出现爆掉情况。

var combinationSum2 = function (candidates, target) {
  let res = [];
  candidates.sort((a, b) => a - b);
  let dfs = (t, start, sum) => {
    if (sum >= target) { // 加这外层,超出范围了也终止,防爆栈
      if (sum === target) {
        res.push(t);
      }
      return;
    }
    // 组合
    for (let i = start; i < candidates.length; i++) {
      // 组合元素不能重复,去掉同一层重复的元素
      if (i > start && candidates[i] == candidates[i - 1]) continue;
      t.push(candidates[i]);
      // 组合元素去重,即当前选择和下一层的不能重复
      dfs(t.slice(), i + 1, sum + candidates[i]);
      t.pop();
    }
  }
  dfs([], 0, 0);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 93. 复原IP地址

仰望星空的人,不应该被嘲笑

题目描述

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

例如:"0.1.2.201" 和 "192.168.1.1" 是 有效的 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "[email protected]" 是 无效的 IP 地址。

示例 1:

输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]

示例 2:

输入:s = "0000"
输出:["0.0.0.0"]

示例 3:

输入:s = "1111"
输出:["1.1.1.1"]

示例 4:

输入:s = "010010"
输出:["0.10.0.10","0.100.1.0"]

示例 5:

输入:s = "101023"
输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
 

提示:

0 <= s.length <= 3000
s 仅由数字组成

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/restore-ip-addresses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

直接看图解,显然要用回溯来做,我的做法是对于当前位置,我们可以有三种选择,选一个,选两个,还有选三个。此时就需要判断一下是不是会出现选出边界的情况。

然后对于我们选择的数字,要判断是否出现前导 0 ,同时也要看一下如果是三位数字的话,是不是会超过 255 。题目不能重复选择,于是用组合**,免去 vis 数组。

借助大佬 xiao_ben_zhu 图解

var restoreIpAddresses = function (s) {
  let res = [];
  let dfs = (cur, start) => {
    if (cur.length == 4 && start>=s.length) {
      res.push(cur.join('.'));
      return;
    }
    if(cur.length == 4 && start != s.length) return;
    for(let k=1;k<=3;k++){
      // 如果取的范围超过了字符串长度,直接剪掉
      if(start+k-1>=s.length) return;
      // 切割字符串
      let str = s.substring(start,start+k);
      if(str.length>=2 && str[0] == 0) return;
      if(str.length>=3 && +str > 255) return;
      cur.push(str);
      dfs(cur.slice(),start+k);
      // 回溯
      cur.pop();
    }
  }
  dfs([], 0);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 104. 二叉树的最大深度

仰望星空的人,不应该被嘲笑

题目描述

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

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

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

示例:

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

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

dfs,通过后续遍历求得,但是需要注意最后结果要加上根节点(即加1),并且需要判断一下树是不是为空,树为空的话直接返回 0 即可,不加 1

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function (root) {
    if(!root) return 0;
    let dfs = (root) => {
        if (root == null) return 0;
        // 后续遍历
        let left = root.left && dfs(root.left) + 1;
        let right = root.right && dfs(root.right) + 1;

        return Math.max(left, right);
    }
    // 后续遍历结果还要加上根节点(即加1)
    return dfs(root) + 1;
};

解法二

bfs,一层一层访问,每加一层计数器就加1,这样到达最后一层了直接返回我们的计数器结果即可。

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function(root) {
    if(!root) return 0
    let queue =[root]
    let cnt = 0
    while(queue.length){
        let size = queue.length
        while(size--){
            let node = queue.shift()
            if(node.left) queue.push(node.left)
            if(node.right) queue.push(node.right)
        }
        ++cnt
    }
    return cnt
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 108. 将有序数组转换为二叉搜索树

仰望星空的人,不应该被嘲笑

题目描述

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

题目要求高度平衡——构建 root 时,选数组的中间元素作为 root 节点值,即可保证平衡。

类似分治算法一样,对中间位置确定根节点 root ,然后递归左子树和右子树,最终走完后就是我们的高度平衡的子树。

参考 笨猪爆破组 大佬题解

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {number[]} nums
 * @return {TreeNode}
 */
var sortedArrayToBST = function (nums) {
    let buildBST = (nums, start, end) => {
        if (start > end) return null;
        // 找到中间位置
        let mid = (start + end) >>> 1;
        // 创建根节点
        let root = new TreeNode(nums[mid]);
        // 构建左子树
        root.left = buildBST(nums, start, mid - 1);
        // 构建右子树
        root.right = buildBST(nums, mid + 1, end);
        return root;
    }
    return buildBST(nums,0,nums.length-1);
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 112. 路径总和

仰望星空的人,不应该被嘲笑

题目描述

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

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

示例:

给定如下二叉树,以及目标和 sum = 22

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

dfs,对于非叶子节点,我们直接减去相应权值,到达了叶子节点,我们判断一下即可,如果满足条件,返回 true

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} sum
 * @return {boolean}
 */
var hasPathSum = function (root, sum) {
    if(!root) return false;
    let res = false;
    let dfs = (sum, root) => {
        // 非叶子节点,就减去权值
        sum -= root.val;
        // 到达叶子节点,进行判断
        if (!root.left && !root.right) {
            if (sum === 0) {
                res = true;
                return;
            }
        }
        // 先遍历左子树,再遍历右子树
        root.left && dfs(sum, root.left);
        root.right && dfs(sum, root.right);
    }
    dfs(sum, root);
    return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 236. 二叉树的最近公共祖先

仰望星空的人,不应该被嘲笑

题目描述

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

=

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3

示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明:

所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

根据定义,我们知道,假设 rootp、q 的最近公共祖先,则只可能有如下几种情况:

  • p、q 在 root 的子树中,那么 p、q 分在 root 的左右子树中
  • 如果 p = root,那么 q 会在 p 的左右子树中,直接返回 p 即可
  • 如果 q = root,与上述类似

然后我们采用 后序遍历的方式,先遍历左右子树,看能不能找到对应 pq 节点。

如果左右子树都能找到,那么代表p、q 分在 root 的左右子树中,直接返回 root 节点
如果左子树找到,右子树没找到,那么就返回左子树的查找结果
如果右子树找到,左子树没找到,那么就返回右子树的查找结果

参考 Krahets 大佬图解

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function (root, p, q) {
    if (!root || root == p || root == q) return root;
    let left = lowestCommonAncestor(root.left, p, q);
    let right = lowestCommonAncestor(root.right, p, q);
    // 如果当前root 在左右子树下可以找到 p q 那么这个root就是它们的最近公共祖先
    if(left && right) return root; 
    // 如果当前root 只有左子树有节点,那么这个节点本身就是p、q的祖先
    else if(left) return left;
    // 如果当前root 只有右子树有节点,那么这个节点本身就是p、q的祖先
    else if(right) return right;
    // 如果左右子树都找不到p、q,那么这颗树不存在这两个点,直接返回 null 即可
    //(但本题告知已存在,所以下面这一行代码可写可不写都能通过)
    return null;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 199. 二叉树的右视图

仰望星空的人,不应该被嘲笑

题目描述

给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

示例:

输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:

   1            <---
 /   \
2     3         <---
 \     \
  5     4       <---

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-right-side-view
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

DFS,每一层只能取一个元素,那么我们搜的时候优先考虑右孩子即可。

参考 shetia 大佬图解

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var rightSideView = function(root) {
    if(!root) return [];
    let res = [];
    let dfs = (step,root) => {
        if(res.length === step){
            res.push(root.val);
        }
        // 优先遍历右孩子,再遍历左孩子
        root.right && dfs(step+1,root.right);
        root.left && dfs(step+1,root.left);
    }
    dfs(0,root);
    return res;
};

解法二

BFS,对于每一层取队列中对首元素,然后放入队列的时候,如果有对应左右孩子的话,优先放右孩子,再放左孩子。

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var rightSideView = function (root) {
    if (!root) return [];
    let queue = [root];
    let res = [];
    while (queue.length) {
        let size = queue.length;
        res.push(queue[0].val);
        while (size--) {
            let node = queue.shift();
            // 优先放右孩子
            node.right && queue.push(node.right);
            node.left && queue.push(node.left);
        }
    }
    return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 73. 矩阵置零

仰望星空的人,不应该被嘲笑

题目描述

给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。

示例 1:

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

示例 2:

输入: 
[
  [0,1,2,0],
  [3,4,5,2],
  [1,3,1,5]
]
输出: 
[
  [0,0,0,0],
  [0,4,5,0],
  [0,3,1,0]
]

进阶:

一个直接的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个常数空间的解决方案吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/set-matrix-zeroes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

用 O(n) 空间复杂度来做,先遍历矩阵,找到等于0的坐标,然后遍历坐标,将对应行和列置为 0 即可

时间复杂度 O(m * n)

var setZeroes = function(matrix) {
    let n = matrix.length
    let m = matrix[0].length
    let arr = []
    for(let i=0;i<n;i++){
        for(let j=0;j<m;j++){
            if(matrix[i][j] == 0){
                arr.push([i,j])
            }
        }
    }
    while(arr.length){
        let [x,y] = arr.pop()
        for(let i=0;i<n;i++) matrix[i][y] = 0
        for(let j=0;j<m;j++) matrix[x][j] = 0
    }
    return matrix
};

另外一种,原地算法,空间复杂度 O(1),我们无需借助外部空间。找到下标为 0 的坐标,然后直接对该行和该列不等于 0 的数字设置为 -0 即可。这里巧妙运用了 JS 中的 Object.is()方法,此时 0 -0 不相等,但是最终返回的矩阵还是为 0

var setZeroes = function(matrix) {
    for(let i=0;i<matrix.length;i++){
        for(let j=0;j<matrix[0].length;j++){
            if(Object.is(matrix[i][j],0)){
                // 对行进行操作
                for(let k=0;k<matrix.length;k++)
                    if(!Object.is(matrix[k][j],0) && k!==i) matrix[k][j] = -0
                // 对列进行操作
                for(let k=0;k<matrix[0].length;k++)
                    if(!Object.is(matrix[i][k],0) && k!==j) matrix[i][k] = -0
            }
        }
    }
    return matrix
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 39. 组合总和

仰望星空的人,不应该被嘲笑

题目描述

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。 

示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

提示:

1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

这道题是组合题,但是这道题有意思的是当前元素可以重复无限制选取,那么我们可以改一下另外一道组合题的思路,下一层也从 i开始即可,然后本题元素重复,那么我们不需要进行排序然后剪枝了。

// 当前元素可以无限制选取,下一层也从i开始取
dfs(t.slice(),i,sum+candidates[i]); 


参考xiao_ben_zhu图解

var combinationSum = function(candidates, target) {
  let res = [];
  let dfs = (t,start,sum) => {
    if(sum >= target){ // 防止爆掉
      if(sum === target){
        res.push(t);
      }
      return;
    }
    for(let i=start;i<candidates.length;i++){
      t.push(candidates[i]);
      // 当前元素可以无限制选取,下一层也从i开始取
      dfs(t.slice(),i,sum+candidates[i]); 
      t.pop();
    }
  }
  dfs([],0,0);
  return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 129. 求根到叶子节点数字之和

仰望星空的人,不应该被嘲笑

题目描述

给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。

例如,从根到叶子节点路径 1->2->3 代表数字 123

计算从根到叶子节点生成的所有数字之和。

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

示例 1:

输入: [1,2,3]
    1
   / \
  2   3
输出: 25
解释:
从根到叶子节点路径 1->2 代表数字 12.
从根到叶子节点路径 1->3 代表数字 13.
因此,数字总和 = 12 + 13 = 25.

示例 2:

输入: [4,9,0,5,1]
    4
   / \
  9   0
 / \
5   1
输出: 1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495.
从根到叶子节点路径 4->9->1 代表数字 491.
从根到叶子节点路径 4->0 代表数字 40.
因此,数字总和 = 495 + 491 + 40 = 1026.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-root-to-leaf-numbers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

dfs,从根节点开始搜,一直搜到子节点(即没有左右孩子的节点),那么就返回路径值,如果没有到达叶子节点,那么就对当前节点的左右子树进行递归操作,求累计和。

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var sumNumbers = function (root) {
    let sum = 0;
    let dfs = (cur, root) => {
        // 终止条件
        if (root == null) return 0;
        // 计算当前节点的值
        cur = cur*10 + root.val;
        // 找到一条路径,返回路径和
        if (root.left == null && root.right == null) {
            return cur;
        }
        // 对左右子树递归,求总和
        return dfs(cur,root.left) + dfs(cur,root.right);
    }
    sum = dfs(0, root);
    return sum;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 1291. 顺次数

仰望星空的人,不应该被嘲笑

题目描述

我们定义「顺次数」为:每一位上的数字都比前一位上的数字大 1 的整数。

请你返回由 [low, high] 范围内所有顺次数组成的 有序 列表(从小到大排序)。

示例 1:

输出:low = 100, high = 300
输出:[123,234]

示例 2:

输出:low = 1000, high = 13000
输出:[1234,2345,3456,4567,5678,6789,12345]
 

提示:

10 <= low <= high <= 10^9

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sequential-digits
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

「顺次数」为:每一位上的数字都比前一位上的数字大 1 的整数。

也就是例如 1234这样的数字,然后给你一段区间确定范围。

官方给了枚举方式,反正数据量也不是很大,但是我觉得还是有很多数字没必要枚举,可以直接剪枝掉。我的做法是先求出最小值和最大值对应字符串的长度,即求出我们能枚举的数字的长度范围。

然后我们的起点的最小值从 1 开始,起点的最大值从 10-len 开始。为什么是 10-len?举例说明,示例1给的是 [100,300]范围的值,那么可枚举的长度 len 为 3,起点的最大值就位 10 - 3 = 7。那么此时顺次数为 789 但是不在我们区间范围内,舍弃。然后8、9开头的数字就不需要枚举了。 这样,我们就能剪掉一部门数据了。(虽然暴力是永远滴神...)

/**
 * @param {number} low
 * @param {number} high
 * @return {number[]}
 */
var sequentialDigits = function(low, high) {
    let res = []
    let lowLen = low.toString().length
    let highLen = high.toString().length
    for(let i=lowLen;i<=highLen;i++){
        for(let j=1;j<=10-i;j++){
            let str = ''
            let num = j
            str += num
            let k = i-1
            while(k--){
                num++
                str += num
            }
            let ans = parseInt(str)
            if(ans>=low && ans<=high){
                res.push(ans)
            }
        }
    }
    return res    
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 面试题 08.08. 有重复字符串的排列组合

仰望星空的人,不应该被嘲笑

题目描述

有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。

示例1:

 输入:S = "qqe"
 输出:["eqq","qeq","qqe"]

示例2:

 输入:S = "ab"
 输出:["ab", "ba"]

提示:

字符都是英文字母。
字符串长度在[1, 9]之间。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-ii-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

全排列,直接用回溯法即可,数据量比较小,暴力完事~

var permutation = function (S) {
  let res = new Set()
  let vis = []
  let dfs = (t) => {
    if (t.length === S.length) return res.add(t)
    for (let i = 0; i < S.length; i++) {
      if (vis[i]) continue
      vis[i] = true
      dfs(t + S[i])
      vis[i] = false
    }
  }
  dfs('')
  return [...res]
}

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

LeetCode 501. 二叉搜索树中的众数

仰望星空的人,不应该被嘲笑

题目描述

给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。

假定 BST 有如下定义:

  • 结点左子树中所含结点的值小于等于当前结点的值
  • 结点右子树中所含结点的值大于等于当前结点的值
  • 左子树和右子树都是二叉搜索树

例如:

给定 BST [1,null,2,2],

   1
    \
     2
    /
   2
返回[2].

提示:如果众数超过1个,不需考虑输出顺序

进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-mode-in-binary-search-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

由于 BST(二叉搜索树)的特殊性,我们采用递归来中序遍历,访问的节点值是有序的。然后重复节点,用计数器进行累加即可,如果有新值出现,则更新新值,然后计数器重置为 1。然后对于当前次数超过了最大值,则更新当前最大值,如果等于最大值,则代表出现了相同频率的数字,加入即可。

如果次数小于最大值,不需要什么操作。

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var findMode = function(root) {
    let cnt = 0;
    let pre = 0;
    let res = [];
    let maxCnt = 0;
    let handle = (cur) => {
        // 相同的数,累加
        if(cur === pre){
            cnt++;
        }else{
            // 有新数出现,重新置计数器为1,更新新数
            pre = cur;
            cnt = 1;
        }
        // 如果次数超过了最大值,更新当前最大值
        if(cnt > maxCnt){
            maxCnt = cnt;
            res = [cur];
        // 如果有相同频率的数字出现,直接加入
        }else if(cnt === maxCnt){
            res.push(cur);
        }
    }
    // 二叉搜索树,递归中序遍历方式
    let inOrder = (root) =>{
        if(!root) return null;
        inOrder(root.left);
        handle(root.val);
        inOrder(root.right);
    }
    inOrder(root);
    return res;
};

最后

文章产出不易,还望各位小伙伴们支持一波!

往期精选:

小狮子前端の笔记仓库

leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)

小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you!

访问超逸の博客,方便小伙伴阅读玩耍~

学如逆水行舟,不进则退

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.