From 8a1fcdc56e8690dbf39dba9bc7d90ef4b20a4007 Mon Sep 17 00:00:00 2001 From: yk Date: Tue, 13 Feb 2024 17:41:45 +0800 Subject: [PATCH] supplement --- content/posts/2024/01/0115.md | 161 +++++++++++++++++- ...27\347\232\204\345\256\236\347\216\260.md" | 2 +- 2 files changed, 161 insertions(+), 2 deletions(-) diff --git a/content/posts/2024/01/0115.md b/content/posts/2024/01/0115.md index 10aa5bf..01648c5 100644 --- a/content/posts/2024/01/0115.md +++ b/content/posts/2024/01/0115.md @@ -1,7 +1,7 @@ --- title: '0115-二叉树' date: 2024-01-15T21:01:56+08:00 -lastmod: +lastmod: 2024-02-07 17:28:23 tags: [] series: [] categories: [] @@ -414,3 +414,162 @@ public boolean isFullTree(TreeNode root) { ``` > 注意,还是那句话,具体问题具体分析,这种技巧,并不适合每种二叉树的问题,比如,一颗树,让你求整个树的中位数,树形 DP 的方式就做不到,其实就是没有最优子结构。 + +--- + +## 练习题 + +### 最低公共祖先 lc.236, git merge 的原理 + +```js +var lowestCommonAncestor = function (root, p, q) { + let ans = null + const dfs = (node) => { + if (node == null) { + return false + } + const leftRes = dfs(node.left) + const rightRes = dfs(node.right) + // 更容易理解的做法,向左右子树要信息,定义 dfs 返回是否含有 p 或 q + if ( + (leftRes && rightRes) || + ((node.val == p.val || node.val == q.val) && (leftRes || rightRes)) + ) { + ans = node + return + } + if (node.val == p.val || node.val == q.val || leftRes || rightRes) { + return true + } + return false + } + dfs(root) + return ans +} +``` + +```js +// 更加抽象的代码 +var lowestCommonAncestor = function (root, p, q) { + // 定义 traverse 返回遇到 p 或 q 的节点 + const traverse = (root) => { + if (!root) return root + if (root == p) return p + if (root == q) return q + const left = traverse(root.left) + const right = traverse(root.right) + if (left && right) return root + return left ? left : right + } + return traverse(root) +} +``` + +### 二叉树中序后继节点问题 lcr053、lc285(vip) + +顾名思义,最简单的,根据题意中序遍历即可得到答案: + +```js +var inorderSuccessor = function (root, p) { + const stack = [] + let nextIsRes = false + while (stack.length || root) { + while (root) { + stack.push(root) + root = root.left + } + const node = stack.pop() + if (nextIsRes) return node + if (node == p) nextIsRes = true + if (node.right) root = node.right + } + return null +} +``` + +但是面试怎么可能这么简单呢,挑选候选人,当然需要更优解,因此需要探索到新的思路 + +1. 节点有右侧节点,那么根据中序规则,后继节点是 右侧节点的最左边的子节点 +2. 节点无右侧节点,那么根据中序规则,后续节点是 父节点中第一个作为左子节点的节点 + +如过遇上了 BST,则往往有需要利用上 BST 的性质 + +```js +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @return {TreeNode} + */ +var inorderSuccessor = function (root, p) { + if (p.right) { + let p1 = p.right + while (p1 && p1.left) { + p1 = p1.left + } + return p1 + } + let res = null + let p1 = root + while (p1) { + if (p1.val > p.val) { + res = p1 + p1 = p1.left + } else { + p1 = p1.right + } + } + return res +} +``` + +姊妹题:每个节点有一个 parent 节点,怎么找后继节点,原理基本一样,不做过多介绍。 + +### 二叉树序列化和反序列化 lc.297 + +```js +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * Encodes a tree to a single string. + * @param {TreeNode} root + * @return {string} + */ +var serialize = function (root) { + const traverse = (root) => { + if (!root) return '#_' + let res = root.val + '_' + res += traverse(root.left) + res += traverse(root.right) + return res + } + return traverse(root) +} + +/** + * Decodes your encoded data to tree. + * @param {string} data + * @return {TreeNode} + */ +var deserialize = function (data) { + const arr = data.split('_') + const generateTree = (arr) => { + let root = arr.shift() + if (root == '#') return null + root = new TreeNode(Number(root)) + root.left = generateTree(arr) + root.right = generateTree(arr) + return root + } + return generateTree(arr) +} +/** + * Your functions will be called as such: + * deserialize(serialize(root)); + */ +``` diff --git "a/content/posts/algorithm/\344\274\230\345\205\210\351\230\237\345\210\227\347\232\204\345\256\236\347\216\260.md" "b/content/posts/algorithm/\344\274\230\345\205\210\351\230\237\345\210\227\347\232\204\345\256\236\347\216\260.md" index b51f097..9efc72b 100644 --- "a/content/posts/algorithm/\344\274\230\345\205\210\351\230\237\345\210\227\347\232\204\345\256\236\347\216\260.md" +++ "b/content/posts/algorithm/\344\274\230\345\205\210\351\230\237\345\210\227\347\232\204\345\256\236\347\216\260.md" @@ -11,7 +11,7 @@ JavaScript 中没有内置优先队列这个数据结构,需要自己来实现 ```JavaScript class PriorityQueue { constructor(data, cmp) { - // 使用堆顶守卫,更方便上浮时父节点的获取 p = i << 1, 子节点本身就比较好获取倒是无所谓 + // 使用堆顶守卫,更方便上浮时父节点的获取 p = i >> 1, 子节点本身就比较好获取倒是无所谓 this.data = [null, ...data]; this.cmp = cmp; for (let i = this.data.length >> 1; i > 0; --i) this.down(i); // 对除最后一层的子节点进行堆化初始化