<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Binary Tree on Yosgi</title><link>https://yosgi.github.io/zh/tags/binary-tree/</link><description>Recent content in Binary Tree on Yosgi</description><generator>Hugo</generator><language>zh</language><lastBuildDate>Mon, 29 Jun 2026 09:20:52 +0000</lastBuildDate><atom:link href="https://yosgi.github.io/zh/tags/binary-tree/index.xml" rel="self" type="application/rss+xml"/><item><title>1104-二叉树寻路</title><link>https://yosgi.github.io/zh/post/1104-%E4%BA%8C%E5%8F%89%E6%A0%91%E5%AF%BB%E8%B7%AF/</link><pubDate>Thu, 29 Jul 2021 00:00:00 +1200</pubDate><guid>https://yosgi.github.io/zh/post/1104-%E4%BA%8C%E5%8F%89%E6%A0%91%E5%AF%BB%E8%B7%AF/</guid><description>第一时间想的是构造二叉树，然后利用每层对称的关系 ，求出父节点 var pathInZigZagTree = function(label) { let stack = [] for(let i = 1 ; i &lt;= label ; i ++) { let level = Math.floor(Math.log(i) / Math.log(2)) if (!stack[level]) { stack[level] = [] } if (level % 2 === 0) { stack[level].push(i) } else { stack[level].unshift(i) } } var level = Math.floor(Math.log(label) / Math.log(2)) let ans = [] while (level) { ans.push(label) level-- label = Math.floor(label / 2) let index = stack[level ].length - 1 - stack[level ].indexOf(label) label = stack[level][index] } ans.push(1) return ans.reverse() };很遗憾，超时了。 受到答案的启发，既然是对称的，同层数时，对称的数之和相同。 一层的首项为 2^ n ，末项是 2^(n + 1) - 1 可的对称数 2^ n + 2^(n + 1) - 1 - x 即 ( 1 &lt;&lt; row ) + ( 1 &lt;&lt; (row + 1) ) - 1 - label; 查找的末位一定是1，可做while 的结束 var pathInZigZagTree = function(label) { let row = Math.floor( Math.log(label) / Math.log(2) ) let ans = [] while(label!==1) { ans.unshift(label) row -- label = getReverse(Math.floor(label / 2),row) } ans.unshift(1) return ans };const getReverse = (label, row) =&gt; { return (1 &lt;&lt; row ) + (1 &lt;&lt; (row + 1) ) - 1 - label;}</description></item><item><title>865-具有所有最深节点的最小子树</title><link>https://yosgi.github.io/zh/post/865-%E5%85%B7%E6%9C%89%E6%89%80%E6%9C%89%E6%9C%80%E6%B7%B1%E8%8A%82%E7%82%B9%E7%9A%84%E6%9C%80%E5%B0%8F%E5%AD%90%E6%A0%91/</link><pubDate>Sun, 21 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/865-%E5%85%B7%E6%9C%89%E6%89%80%E6%9C%89%E6%9C%80%E6%B7%B1%E8%8A%82%E7%82%B9%E7%9A%84%E6%9C%80%E5%B0%8F%E5%AD%90%E6%A0%91/</guid><description>用时:60min 被题目的深度误导，认为计算深度需要由上至下。实际上可以由下至上。 如果左右子树高度相同，返回节点本身以及深度 如果左子树比较深，说明最小最深在左子树，返回左子树以及自己的深度 如果右子树比较深，说明最小深度在右边，返回右子树以及自身深度 var subtreeWithAllDeepest = function(root) { var dfs = function (root) { if (!root) return [root,0] var [left,ld] = dfs(root.left) var [right,rd] = dfs(root.right) if (ld &gt; rd) return [left, ld + 1] if (rd &gt; ld) return [right,rd + 1] return [root,ld + 1] } var res = dfs(root) return res[0] };</description></item><item><title>783-二叉搜索树节点最小距离</title><link>https://yosgi.github.io/zh/post/783-%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E8%8A%82%E7%82%B9%E6%9C%80%E5%B0%8F%E8%B7%9D%E7%A6%BB/</link><pubDate>Thu, 18 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/783-%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E8%8A%82%E7%82%B9%E6%9C%80%E5%B0%8F%E8%B7%9D%E7%A6%BB/</guid><description>用时 ： 10min 利用前序遍历二叉树递增的性质解决 var minDiffInBST = function(root) { var min = Infinity var pre = null var dfs = function (root) { if (!root) return if (root.left) { dfs(root.left) } if (pre == null) { pre = root.val } else { console.log(pre,root.val) var reduce = Math.abs(pre - root.val) min = Math.min(min,reduce) pre = root.val } if (root.right) { dfs(root.right) } } dfs(root) return min };</description></item><item><title>814-二叉树剪枝</title><link>https://yosgi.github.io/zh/post/814-%E4%BA%8C%E5%8F%89%E6%A0%91%E5%89%AA%E6%9E%9D/</link><pubDate>Wed, 17 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/814-%E4%BA%8C%E5%8F%89%E6%A0%91%E5%89%AA%E6%9E%9D/</guid><description>用时：6min 很简单，中序遍历，计算两边子树是否存在 1 的值，没有就去掉。凡事左右子树或者自身包含 1 ，返回 true 。否则返回 false var pruneTree = function(root) { var DFS = function (root) { if (!root) return false var left = DFS(root.left) var right = DFS(root.right) if (!left) { root.left = null } if (!right) { root.right = null } if (left) { return true } if (right) { return true } if( root.val === 1) { return true } } var dummp = new TreeNode(1) dummp.left = root DFS(dummp) return dummp.left};一个需要注意的点是，root本身也有可能需要剪掉，所以增加了一个虚拟节点来作为父节点</description></item><item><title>1448-统计二叉树中好节点的数目</title><link>https://yosgi.github.io/zh/post/1448-%E7%BB%9F%E8%AE%A1%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E5%A5%BD%E8%8A%82%E7%82%B9%E7%9A%84%E6%95%B0%E7%9B%AE/</link><pubDate>Wed, 17 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/1448-%E7%BB%9F%E8%AE%A1%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E5%A5%BD%E8%8A%82%E7%82%B9%E7%9A%84%E6%95%B0%E7%9B%AE/</guid><description>用时： 偷偷看了答案 本来把问题想复杂了，想着维护一个最大堆，从上往下传递然后每次判断是否大于最大值 看了答案后发现只需要传递一个max，判断是否比max大，然后从下到上获取左右节点的值，返回左右节点和 加自身是否符合就好了 var goodNodes = function(root,max = root.val) { if (!root) return 0 var left = 0 max = Math.max(max,root.val) if (root.left) { left = goodNodes(root.left,max) } var right = 0 if (root.right) { right = goodNodes(root.right,max) } console.log(left,right,root.val) return (left + right) + (root.val &gt;= max ? 1 : 0) };</description></item><item><title>1325-删除给定值的叶子节点</title><link>https://yosgi.github.io/zh/post/1325-%E5%88%A0%E9%99%A4%E7%BB%99%E5%AE%9A%E5%80%BC%E7%9A%84%E5%8F%B6%E5%AD%90%E8%8A%82%E7%82%B9/</link><pubDate>Wed, 17 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/1325-%E5%88%A0%E9%99%A4%E7%BB%99%E5%AE%9A%E5%80%BC%E7%9A%84%E5%8F%B6%E5%AD%90%E8%8A%82%E7%82%B9/</guid><description>用时：5min 虚拟节点 + 后序遍历 就可以愉快的秒了 var removeLeafNodes = function(root, target) { var dfs = function (root) { if (root.left) { root.left = dfs(root.left) } if (root.right) { root.right = dfs(root.right) } if (!root.left &amp;&amp; !root.right) { if (root.val === target) { root = null } } return root } var dummy = new TreeNode(0) dummy.left = root dfs(dummy) return dummy.left};</description></item><item><title>1022-从根到叶的二进制数之和</title><link>https://yosgi.github.io/zh/post/1022-%E4%BB%8E%E6%A0%B9%E5%88%B0%E5%8F%B6%E7%9A%84%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E4%B9%8B%E5%92%8C/</link><pubDate>Wed, 17 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/1022-%E4%BB%8E%E6%A0%B9%E5%88%B0%E5%8F%B6%E7%9A%84%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E4%B9%8B%E5%92%8C/</guid><description>用时：10min 前序从上至下计算，没什么好说的 二进制转10 进制挺费劲的，我直接parseInt(path, 2) var sumRootToLeaf = function(root) { var ans = 0 var dfs = function (root,path) { if (!root) return path += root.val if (!root.left &amp;&amp; !root.right) { ans += parseInt(path, 2) } root.left &amp;&amp; dfs(root.left,path) root.right &amp;&amp; dfs(root.right,path) } dfs(root,'') return ans };</description></item><item><title>563-二叉树的坡度</title><link>https://yosgi.github.io/zh/post/563-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%9D%A1%E5%BA%A6/</link><pubDate>Tue, 16 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/563-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%9D%A1%E5%BA%A6/</guid><description>用时：15min 用时：15min 拿到题的第一反应是是用递归，然后直接开始写了后发现 递归需要满足的条件是问题可以拆分成子问题，但是根据题意，我们需要求的是每个节点左右节点和的差值，这个 “左右节点和” 对于每个节点来说，都是不一样的问题 所以还是双递归 var findTilt = function(root) { var total = 0 var innerDFS = function (root) { if (!root) return 0 var left = 0 ,right = 0 if (root.left) { left = innerDFS(root.left) } if (root.right) { right = innerDFS(root.right) } return root.val + left + right } var outerDFS = function (root) { if (!root) return root.right &amp;&amp; outerDFS(root.right) total += Math.abs(innerDFS(root.left) - innerDFS(root.right)) root.left &amp;&amp; outerDFS(root.left) } outerDFS(root) return total };But!!!!!!!! 看了一眼答案我觉得自己还是拿衣服了 原来计算左右节点之和，与累积左右节点只差并不矛盾，一个递归就能解决问题。就是DFS的时候顺便两者一起做了，累计左右节点就是 return root.val + left + right 而计算差值是 total += Math.abs(left-right) var findTilt = function(root) { var total = 0 var DFS = function (root) { if (!root) return 0 var left = 0 ,right = 0 if (root.left) { left = DFS(root.left) } if (root.right) { right = DFS(root.right) } total += Math.abs(left-right) return root.val + left + right } DFS(root) return total };还是年轻了，明明做过类似的递归</description></item><item><title>129-求根节点到叶节点数字之和</title><link>https://yosgi.github.io/zh/post/129-%E6%B1%82%E6%A0%B9%E8%8A%82%E7%82%B9%E5%88%B0%E5%8F%B6%E8%8A%82%E7%82%B9%E6%95%B0%E5%AD%97%E4%B9%8B%E5%92%8C/</link><pubDate>Tue, 16 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/129-%E6%B1%82%E6%A0%B9%E8%8A%82%E7%82%B9%E5%88%B0%E5%8F%B6%E8%8A%82%E7%82%B9%E6%95%B0%E5%AD%97%E4%B9%8B%E5%92%8C/</guid><description>看题型是求从根节点出发的，首先想到的是自顶向下的DFS，带着参数向下传递，结束条件没有左右儿子 直接前序遍历走起，可以认为是前面的节点都处理好了 var sumNumbers = function(root) { var ans = 0 var DFS = function (root,path) { if (!root) return path += root.val if (!root.left &amp;&amp; !root.right) { ans += Number(path) } root.left &amp;&amp; DFS(root.left,path) DFS(root.right, path) } DFS(root,'') return ans };</description></item><item><title>124-二叉树中的最大路径和</title><link>https://yosgi.github.io/zh/post/124-%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E7%9A%84%E6%9C%80%E5%A4%A7%E8%B7%AF%E5%BE%84%E5%92%8C/</link><pubDate>Mon, 15 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/124-%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E7%9A%84%E6%9C%80%E5%A4%A7%E8%B7%AF%E5%BE%84%E5%92%8C/</guid><description>用时 ：30min 看懂题目的意思之后其实就能很快的做出来 题意是要求从任意节点出发的值，我们可以把每个节点的值求出来的过程中，选择最大的 又因为路径不能返回，可以知道我们出发的根节点的子节点的路径只能选择左或者右，或者都不要，结果为 x 求max 的时候再 把 x 和 左右都选的情况进行比较 var maxPathSum = function(root) { var max = -Infinity var DFS = function (root) { if (!root) return 0 var leftSum = DFS(root.left) var rightSum = DFS(root.right) var res = Math.max(leftSum + root.val ,rightSum + root.val ,root.val) max = Math.max(max,res,root.val + leftSum + rightSum) return res } DFS(root) return max };</description></item><item><title>99-恢复二叉搜索树</title><link>https://yosgi.github.io/zh/post/99-%E6%81%A2%E5%A4%8D%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</link><pubDate>Sat, 13 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/99-%E6%81%A2%E5%A4%8D%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</guid><description>用时：60min 思路还是中序遍历，使用pre缓存前一个节点，因为中序遍历是递增的，所以一定是先找到大的，再找到小的。 所以第一个出问题的是pre，第二个是root var recoverTree = function(root) { var left = null var right = null var pre = null // 一定是先找到大的，再找到小的，所以第一个出问题的是pre，第二个出问题的是root var DFS = function (root) { if (root.left) { DFS(root.left) } if (pre) { console.log(pre.val) if (pre.val &gt; root.val) { if(!left) { left = pre right = root } else { right = root } } pre = root } else { pre = root } if (root.right) { DFS(root.right) } } DFS(root) if (left &amp;&amp; right) { [left.val,right.val] = [right.val,left.val] } return root}</description></item><item><title>98-验证二叉搜索树</title><link>https://yosgi.github.io/zh/post/98-%E9%AA%8C%E8%AF%81%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</link><pubDate>Sat, 13 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/98-%E9%AA%8C%E8%AF%81%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</guid><description>用时 10min 直接用二叉搜索树中序遍历递增的性质就好了 中序遍历找到左孩子 pre 是否存在 不存在，赋值 ，找右孩子，返回 1 存在，是否比 val 小 是，非递增，返回 false 否，递增，赋值 pre ，返回 1 var isValidBST = function(root) { var preVal = null var DFS = function(root) { var left = true var right = true if (root.left) { left = DFS(root.left) } if (preVal !== null) { if (preVal &gt;= root.val) return false preVal = root.val } else { preVal = root.val } if (root.right) { right = DFS(root.right) } return left &amp;&amp; right } return DFS(root) };</description></item><item><title>662-二叉树最大宽度</title><link>https://yosgi.github.io/zh/post/662-%E4%BA%8C%E5%8F%89%E6%A0%91%E6%9C%80%E5%A4%A7%E5%AE%BD%E5%BA%A6/</link><pubDate>Sat, 13 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/662-%E4%BA%8C%E5%8F%89%E6%A0%91%E6%9C%80%E5%A4%A7%E5%AE%BD%E5%BA%A6/</guid><description>用时：120min 一开始的思路是用BFS，出队时出一整队（后来知道这叫宽度遍历） 然后双指针找到左右节点。当左右节点都不存在时结束 var widthOfBinaryTree = function(root) { var que = [root] var max = 0 while(que.length) { var len = que.length var left = 0 var right = que.length - 1 while(!que[left] &amp;&amp; left &lt; len) { left ++ } while(!que[right] &amp;&amp; right &gt;= 0) { right -- } if (left &gt; right) { break } max = Math.max(max,right - left+ 1) for(let i = 0 ; i &lt; len ; i ++) { var node = que.shift() if (!node) { que.push(node) que.push(node) continue } node.left ? que.push(node.left) : que.push(null) node.right ? que.push(node.right) : que.push(null) } } return max };很快啊，提交显示执行超过时间限制，于是增加了一个储存序号的队列，和BFS出栈同步，用于计算左右距离 var widthOfBinaryTree = function(root) { var que = [root] var numQue = [1] var max = 0 while(que.length) { var len = que.length max = Math.max(numQue[numQue.length - 1] - numQue[0] + 1) for(let i = 0 ; i &lt; len ; i ++) { var node = que.shift() var val = numQue.shift() if (node.left) { que.push(node.left) numQue.push(val * 2) } if (node.right) { que.push(node.right) numQue.push(val * 2 + 1) } } } return max };很快啊，告诉我栈溢出了。我这才意识到问题的严重性（误 然后翻了翻答案 对每次的下表统一减去了当层的第一个数 var widthOfBinaryTree = function(root) { var que = [root] var numQue = [0] var max = 0 while(que.length) { var len = que.length var start = numQue[0] max = Math.max( numQue[numQue.length - 1] - start + 1,max) for(let i = 0 ; i &lt; len ; i ++) { var node = que.shift() var val = numQue.shift() - start if (node.left) { que.push(node.left) numQue.push(val * 2 + 1 ) } if (node.right) { que.push(node.right) numQue.push(val * 2 + 2 ) } } } return max };</description></item><item><title>222-完全二叉树的节点个数</title><link>https://yosgi.github.io/zh/post/222-%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E8%8A%82%E7%82%B9%E4%B8%AA%E6%95%B0/</link><pubDate>Sat, 13 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/222-%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E8%8A%82%E7%82%B9%E4%B8%AA%E6%95%B0/</guid><description>用时：80min 拿到题的第一个反应当然是DFS解决…. 但是这是一个完全二叉树，怎么说也应该把完全二叉树的性质用上才行 可以考察二叉树的左右子树深度 如果左深度 大于 右 ， 说明右边已经满了，满了的个数是 2 的 n 次方 - 1，加上root 是 2 的 n次方，继续递归左边 如果右深度大于左， 说明左边满了，同样，继续递归右边 至于怎么求二叉树的深度，可以从下往上递归 var count = function(root) { if (root === null) return 0 return Math.max(count(root.left),count(root.right)) + 1}那么总体的代码就是 var countNodes = function(root) { if (!root) { return 0 } var count = function(root) { if (root === null) return 0 return Math.max(count(root.left),count(root.right)) + 1 } var leftLevel = count(root.left) var rightLevel = count(root.right) if (leftLevel === rightLevel) { return Math.pow(2,leftLevel) + countNodes(root.right) } else { return Math.pow(2,rightLevel ) + countNodes(root.left) } };</description></item><item><title>669-修剪二叉搜索树</title><link>https://yosgi.github.io/zh/post/669-%E4%BF%AE%E5%89%AA%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</link><pubDate>Thu, 11 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/669-%E4%BF%AE%E5%89%AA%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</guid><description>用时 ：20min 与上一道题类似，多的是分了父节点在区间内和不在区间内的情况 var trimBST = function(root, low, high) { var add = function (root,node) { if (!node || !root) return null if (node.val &gt; root.val ) { if (!root.right) { root.right = node } else { add(root.right,node) } } if (node.val &lt; root.val) { if (!root.left) { root.left = node } else { add(root.left,node) } } } var walk = function (root) { if (!root) return root if (root.val &lt; low || root.val &gt; high) { var left = walk(root.left) var right = walk(root.right) if (!left &amp;&amp; !right) { root = null } else if (right &amp;&amp; left) { root = right add(root,left) } else if (left) { root = left } else { root = right } } else { root.left= walk(root.left) root.right = walk(root.right) } return root } return walk(root) };看了解析之后发现可以更简单，思路是如果大于右边界，就直接在左分支找； 如果小于左边界，就直接在右分支找。这样就省去了删除节点的步骤 var trimBST = function(root, low, high) { if(root == null) return null if (root.val &gt; high) return trimBST(root.left,low,high) if(root.val &lt; low) return trimBST(root.right,low,high) root.left = trimBST(root.left,low,height) root.right = trimBST(root.right,low,height) return root};</description></item><item><title>450-删除二叉搜索树中的节点</title><link>https://yosgi.github.io/zh/post/450-%E5%88%A0%E9%99%A4%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9/</link><pubDate>Thu, 11 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/450-%E5%88%A0%E9%99%A4%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9/</guid><description>用时:30min 题目分两个部分需要解决，搜索和删除 搜索部分就是找到节点，可以根据二叉搜索树的性质来找到需要的节点 删除分三种情况 只有左孩子，用左孩子代替 只有右孩子，用右孩子代替 都没有，则赋值为null （ 由于前面有递归赋值，所以这边可以成功 都有，则储存左孩子后，用右孩子代替，然后找到左孩子在右边的位置 var deleteNode = function(root, key) { function insert(root,node) { if (root.val &gt; node.val) { if (!root.left) { root.left = node } else { insert(root.left,node) } } else { if (!root.right) { root.right = node } else { insert(root.right,node) } } } function search(root) { if (!root) { return null } if (root.val &gt; key) { root.left = search(root.left) } else if (root.val &lt; key){ root.right = search(root.right) } else { if (!root.left &amp;&amp; !root.right) { root = null } else if (root.left &amp;&amp; root.right) { var left = root.left root = root.right insert(root,left) } else if (root.left) { root = root.left } else if (root.right) { root = root.right } } return root } return search(root) };需要注意的点 考虑节点不存在的情况 删除一个节点的方法，可以给左（右 节点赋值为递归的返回值</description></item><item><title>116-填充每个节点的下一个右侧节点指针</title><link>https://yosgi.github.io/zh/post/116-%E5%A1%AB%E5%85%85%E6%AF%8F%E4%B8%AA%E8%8A%82%E7%82%B9%E7%9A%84%E4%B8%8B%E4%B8%80%E4%B8%AA%E5%8F%B3%E4%BE%A7%E8%8A%82%E7%82%B9%E6%8C%87%E9%92%88/</link><pubDate>Wed, 10 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/116-%E5%A1%AB%E5%85%85%E6%AF%8F%E4%B8%AA%E8%8A%82%E7%82%B9%E7%9A%84%E4%B8%8B%E4%B8%80%E4%B8%AA%E5%8F%B3%E4%BE%A7%E8%8A%82%E7%82%B9%E6%8C%87%E9%92%88/</guid><description>用时 ：25min 主要思路是利用栈，和普通的层序遍历不同的是每次都把栈清空 var connect = function(root) { if (!root) return null var stack = [root] while(stack.length) { var _stack = [...stack,null] stack = [] var pre = _stack.shift() while(_stack.length || pre) { pre.left &amp;&amp; stack.push(pre.left) pre.right &amp;&amp; stack.push(pre.right) pre.next = _stack.shift() pre = pre.next } } return root};很显然我使用的 On 的内存空间是不符合题目的常数空间的，考虑到每一层的链接可以依靠父节点，以链表的形式找到。如下代码 while(pre) { var cur = pre while(cur) { cur.left.next = cur.right cur.right.next = cur.next.left cur = cur.next } }剩下的需要考虑的有 pre需要保存为最左边的节点，以便于后面以链表的形式进行连接 第一层的next为空 最后一层没有左右节点</description></item><item><title>1008-前序遍历构造二叉搜索树</title><link>https://yosgi.github.io/zh/post/1008-%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</link><pubDate>Wed, 10 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/1008-%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</guid><description>用时 ： 没做出来 没做出来的主要原因是一直想着怎么用栈来做，写到后面看栈的答案看着看着发现递归最简单 递归 思路很简单，首先写出来怎么插入子节点 如果比父节点小，且父节点左为空，直接做左节点 如果比父节点大，且父节点右为空，直接做右节点 比父节点小，则父节点赋值为父节点的左节点，回到 1 比父节点大，则父节点赋值为父节点的右节点，回到1 遍历整个数组，一个个插入即可得到结果 var bstFromPreorder = function(preorder) { var add = function (node,val) { if (val &lt; node.val &amp;&amp; !node.left) { node.left = new TreeNode(val) } if (val &gt; node.val &amp;&amp; !node.right) { node.right = new TreeNode(val) } if (val &lt; node.val) { add(node.left,val) } if (val &gt; node.val) { add(node.right,val) } } var root = new TreeNode(preorder.shift()) for(let i = 0 ; i &lt; preorder.length ; i ++) { add(root,preorder[i]) } return root};</description></item><item><title>894-所有可能的满二叉树</title><link>https://yosgi.github.io/zh/post/894-%E6%89%80%E6%9C%89%E5%8F%AF%E8%83%BD%E7%9A%84%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91/</link><pubDate>Sun, 07 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/894-%E6%89%80%E6%9C%89%E5%8F%AF%E8%83%BD%E7%9A%84%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91/</guid><description>用时： 抄答案才做出来 思路很容易看出来是利用递归，但是coding才是真正的问题。 我们需要返回的是根节点，但是递归是从下至上的 尝试用动态规划解决，dp(n) 是 dp(x) 与 dp (n - x - 1) 结果的组合 var allPossibleFBT = function(n) { var dp = [] var buld = function (n) { for(let i = 1 ; i &lt;= n ; i ++ ) { if (i === 1) { dp[i] = new TreeNode(0) } else if (i % 2 === 0) { dp[i] = undefined } else { dp[i] = [] for(let left = 1; left &lt; i; left ++) { var leftNodes = dp[left] var rightNodes = dp[i - left - 1] for(let j = 0 ; j &lt; leftNodes.length ; j ++) { for(let k = 0 ; k &lt; rightNodes.length ; k ++) { var node = new TreeNode(0) node.left = leftNodes[j] node.right = rightNodes[k] dp[i].push(node) } } } } } } buld(n) return dp[n] };</description></item><item><title>297-二叉树的序列化与反序列化</title><link>https://yosgi.github.io/zh/post/297-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8E%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/</link><pubDate>Thu, 04 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/297-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8E%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/</guid><description>用时：30min 序列化很简单，使用BFS可以解决 反序列化需要用到二叉树的性质，即第 i 个节点的子节点分别为 (i + 1) * 2 - 1 和 ( i + 1) * 2, 流程 找到父节点，入栈，此时指针 i 在 父节点的 val 上 父节点出栈，找到父节点的左右节点，指针根据规则找到 两遍的值，左右节点入栈， i++ 重复 1 var serialize = function(root) { if (!root) return [] var que = [root] var res = [] while(que.length) { var cur = que.shift() if (cur) { res.push(cur.val); que.push(cur.left); que.push(cur.right); } else { res.push('null'); } } return res.join(',') };/** * Decodes your encoded data to tree. * * @param {string} data * @return {TreeNode} */var deserialize = function(data) { if (!data.length) return null var nodes = data.split(',') var i = 0 var root = new TreeNode(nodes[i]) var que = [root] while ( que.length) { var node = que.shift() var left = nodes[ (i + 1) * 2 - 1] var right = nodes[ (i + 1) * 2] if (left !== 'null') { node.left = new TreeNode(left) que.push(node.left) } else { node.left = null } if (right !== 'null') { node.right = new TreeNode(right) que.push(node.right) } else { node.right = null } i++ } return root};</description></item><item><title>1372-二叉树中的最长交错路径</title><link>https://yosgi.github.io/zh/post/1372-%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E7%9A%84%E6%9C%80%E9%95%BF%E4%BA%A4%E9%94%99%E8%B7%AF%E5%BE%84/</link><pubDate>Mon, 01 Mar 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/1372-%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E7%9A%84%E6%9C%80%E9%95%BF%E4%BA%A4%E9%94%99%E8%B7%AF%E5%BE%84/</guid><description>用时：25min 其实是比较常规的DFS类型题目，之所以用这么久是因为对边界值的判断出了问题。 从 root 出发时， 因为root 是可能没有子节点的，所以 len 值 为 0 从子节点出发，因为已经判断完字节点，算作当前节点到下一节点已经走一条长度为 1 的边 ， 所以是 1 var longestZigZag = function(root) { // true left false right var max = 0 var dfs = function (node,flag,len) { max = Math.max(len,max) if (flag) { node.right &amp;&amp; dfs(node.right,!flag,len + 1) node.left &amp;&amp; dfs(node.left,flag,1) } else { node.left &amp;&amp; dfs(node.left,!flag,len + 1) node.right &amp;&amp; dfs(node.right,flag,1) } } dfs(root,true,0) dfs(root,false,0) return max };</description></item><item><title>关于二叉树的总结</title><link>https://yosgi.github.io/zh/post/%E5%85%B3%E4%BA%8E%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%80%BB%E7%BB%93/</link><pubDate>Sun, 28 Feb 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/%E5%85%B3%E4%BA%8E%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%80%BB%E7%BB%93/</guid><description>为什么前中后序遍历用栈，而层序遍历使用队列 ？ 二叉树的前中后序遍历的迭代时，我们用栈来简化操作，因为它们都是DFS的递归结构，也就是从下到上处理，但是我写代码一定是从root节点开始，所以需要栈，而栈正好是先进先出的。这样我才可以把处理root放在最后。 层序遍历是BFS，由上至下。最先入队的root也是我想最先处理的。这就是为什么DFS 使用栈而BFS使用队列的原因。 DFS的算法流程与模板 首先将根节点放入stack中。 从stack中取出第一个节点，并检验它是否为目标。如果找到所有的节点，则结束搜寻并回传结果。否则将它某一个尚未检验过的直接子节点加入stack中。 重复步骤 2。 如果不存在未检测过的直接子节点。将上一级节点加入stack中。 重复步骤 2。 重复步骤 4。 若stack为空，表示整张图都检查过了——亦即图中没有欲搜寻的目标。结束搜寻并回传”找不到目标”。 function dfs(root) { if (满足特定条件）{ // 返回结果 or 退出搜索空间 } for (const child of root.children) { dfs(child) } }BFS 的算法流程与模板 首先将根节点放入队列中。 从队列中取出第一个节点，并检验它是否为目标。 如果找到目标，则结束搜索并回传结果。 否则将它所有尚未检验过的直接子节点加入队列中。 若队列为空，表示整张图都检查过了——亦即图中没有欲搜索的目标。结束搜索并回传”找不到目标”。 重复步骤 2。 function bfs(root) { var que = [root] while(que.length) { var node = que.shift() if (node 是我们要找到的) return node node.left &amp;&amp; que.push(node.left) node.right &amp;&amp; que.push(node.right) } }</description></item><item><title>102-二叉树的层序遍历</title><link>https://yosgi.github.io/zh/post/102-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86/</link><pubDate>Sun, 28 Feb 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/102-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86/</guid><description>102.二叉树的层序遍历 遍历本身很简单，需要考虑的问题是怎么标示每一层，这里可以用迭代和递归两种思路 迭代 入队 root ，再入队一个null 表示第 0 层的结束 出队 root ，入队左右节点 ， 如果出队的是null，说明一层结束，再入队一个 null 队列不为空，循环 2 ⚠️ 需要注意的点： 队列最后一项如果不做处理将会一直是null，因此需要增加结束条件 var levelOrder = function(root) { var que = [root,null] var res = [] var level = [] if (!root ) return [] while(que.length) { var node = que.shift() if (node) { node.left &amp;&amp; que.push(node.left) node.right &amp;&amp; que.push(node.right) level.push(node.val) } else { res.push([...level]) level = [] if(que.length) { que.push(null) } } } return res };递归 递归解法的重点是传递层数作为参数 var levelOrder = function(root) { var res = [] if (!root) return [] var walk = function (node,index) { if (!res[index]) res[index] = [] res[index].push(node.val) node.left &amp;&amp; walk(node.left,index + 1) node.right &amp;&amp; walk(node.right,index + 1) } walk(root,0) return res };步骤如代码所示 为所在的层数开辟一个数组空间 root 加入数组 如果存在左节点，或者右节点，继续到1，层数 + 1</description></item><item><title>145-二叉树的后序遍历</title><link>https://yosgi.github.io/zh/post/145-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%90%8E%E5%BA%8F%E9%81%8D%E5%8E%86/</link><pubDate>Fri, 26 Feb 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/145-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%90%8E%E5%BA%8F%E9%81%8D%E5%8E%86/</guid><description>145.二叉树的后序遍历 1h 中序遍历的顺序是 左 - 右 - 中 递归 用递归非常容易 var postorderTraversal = function(root) { var res = [] if (!root) return res var travel = function (node) { node.left &amp;&amp; travel(node.left) node.right &amp;&amp; travel(node.right) res.push(node.val) } travel(root) return res };迭代 根节点入栈 判断是否可以出栈，如果能，则记录自己为上一个出栈节点，出栈 不能出栈，分别将右节点和左节点入栈 重复第二个步骤 var postorderTraversal = function(root) { var stack = [root] var res = [] if (!root) { return res } var pre = root while(stack.length) { var node = stack[stack.length - 1] // 当节点的 左 / 右 节点是上一个被输出的节点，代表左右节点都已经被输出过（因为后序遍历根节点在最后） if ( (!node.left &amp;&amp; !node.right) || (node.left === pre || node.right === pre)) { // 只有在没有 左右节点，或者 左右节点都输出过时才可以输出 node = stack.pop() pre = node res.push(node.val) } else { if (node.right) { stack.push(node.right) } if(node.left) { stack.push(node.left) } } } return res };总结 虽然，做完了二叉树的前中后序遍历，但是我有预感用不了多久就会忘记，特别是中序遍历和后序遍历，我完全没有找到两者有什么共同点，所以随后会做一下总结。</description></item><item><title>94-二叉树的中序遍历</title><link>https://yosgi.github.io/zh/post/94-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86/</link><pubDate>Thu, 25 Feb 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/94-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86/</guid><description>94.二叉树的中序遍历 30min 中序遍历的顺序是 左 - 中 - 右 递归 用递归非常容易 var inorderTraversal = function(root) { var res = [] if (!root) { return res } var travel = function (node) { node.left &amp;&amp; travel(node.left) res.push(node.val) node.right &amp;&amp; travel(node.right) } travel(root) return res };迭代 迭代的步骤复杂一些，因为根节点不是先输出，所以需要保留根节点 根节点入栈，判断有没有左子节点，如果有，继续入栈，直到叶子结点 出栈，输出，判断是否有右子节点，有则入栈，继续执行2 var inorderTraversal = function(root) { var stack = [] var res = [] if (!root) return res stack.push(root) while(root.left) { stack.push(root.left) root = root.left } while(stack.length) { // 此时栈顶是树中最左的节点 var node = stack.pop() res.push(node.val) // 存在右节点，入栈后继续找左节点 if (node.right) { node = node.right stack.push(node) while(node.left) { stack.push(node.left) node = node.left } } } return res };</description></item><item><title>144-二叉树的前序遍历</title><link>https://yosgi.github.io/zh/post/144-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86/</link><pubDate>Thu, 25 Feb 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/144-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86/</guid><description>144.二叉树的前序遍历 10min 二叉树的前序遍历的顺序是中 - 左 - 右 先遍历完所有的根节点与左节点，然后处理右节点 可以看出来这是一个递归的行为，递归问题可以用栈来进行简化 迭代解法 var preorderTraversal = function(root) { if (!root) return null var stack = [root] var res = [] while(stack.length) { var node = stack.pop() res.push(node.val) node.right &amp;&amp; stack.push(node.right) node.left &amp;&amp; stack.push(node.left) } return res };递归解法 递归问题当然可以递归解决 var preorderTraversal = function(root) { var res = [] if (!root) return res var travel = function (node) { res.push(node.val) node.left &amp;&amp; travel(node.left) node.right &amp;&amp; travel(node.right) } travel(root) return res };</description></item><item><title>105-从前序遍历与中序遍历序列构造二叉树-1</title><link>https://yosgi.github.io/zh/post/105-%E4%BB%8E%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E4%B8%8E%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E5%BA%8F%E5%88%97%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91-1/</link><pubDate>Wed, 27 Jan 2021 00:00:00 +1300</pubDate><guid>https://yosgi.github.io/zh/post/105-%E4%BB%8E%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E4%B8%8E%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E5%BA%8F%E5%88%97%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91-1/</guid><description>递归 前序遍历：中-左-右 中序遍历：左-中-右 因此可以先从前序遍历找到根元素，再由中序确定左右子树的个数 var buildTree = function(preorder, inorder) { if (preorder.length === 0 || inorder.length=== 0) return null let nodeVal = preorder.shift() let node = new TreeNode(nodeVal) let index = inorder.indexOf(nodeVal) node.left = buildTree(preorder.slice(0,index), inorder.slice(0,index)) node.right = buildTree(preorder.slice(index),inorder.slice(index + 1)) return node };优化 slice 是很耗性能的，其实没有必要传递数组，函数传递指针即可 var buildTree = function(preorder, inorder) { var helper = function (p_start,p_end,i_start,i_end) { if (p_start &gt; p_end || i_start &gt; i_end ) return null let nodeVal = preorder[p_start] let node = new TreeNode(nodeVal) let index = inorder.indexOf(nodeVal) let left = index - i_start node.left = helper(p_start + 1 , p_start + left , i_start ,index - 1) node.right = helper(p_start + left + 1,p_end, index + 1,i_end) return node } return helper(0,preorder.length - 1,0,preorder.length - 1) };总结： 利用 前序和中序的性质，定位根节点，求出左右子树的个数。然后递归构建左右子树即可</description></item></channel></rss>