235. 二叉搜索树的最近公共祖先
题目链接:235. 二叉搜索树的最近公共祖先
文档讲解:代码随想录/二叉搜索树的最近公共祖先
视频讲解:视频讲解-二叉搜索树的最近公共祖先
状态:已完成(1遍)
解题过程
看到题目的第一想法
既然这道题是二叉搜索树,意思肯定和普通二叉树的最近共同祖先不同,是有更简便的做法。那么我想一个祖先节点,左子结点都是比自己小的,右子节点都是比自己大的。那么同理,要找的两个节点的祖先节点肯定是在他们之间的。
手搓代码如下:
/**
* 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) {
let ans = null;
const find = function (node) {
if (node == null) return;
if (node.val > p.val && node.val > q.val) {// 向左子树查询
return find(node.left);
}
if (node.val < p.val && node.val < q.val) {// 向右子树查询
return find(node.right);
}
return node;
}
return find(root);
};
提交没有问题。
看完代码随想录之后的想法
逻辑中有句话说的很精髓,我也没考虑到这个问题。从上往下碰到的第一个在两值之间的节点一定是两节点最近的共同祖先,因为碰到的第一个节点,往左走,会错失右边的值;往右走会错失左边的值。详情见视频讲解。
总结
复制一段文字讲解中的总结:
细心的同学会发现,在这里调用递归函数的地方,把递归函数的返回值left,直接return。
在二叉树:公共祖先问题 (opens new window)中,如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树。
搜索一条边的写法:
if (递归函数(root->left)) return ;
if (递归函数(root->right)) return ;
搜索整个树写法:
left = 递归函数(root->left);
right = 递归函数(root->right);
left与right的逻辑处理;
本题就是标准的搜索一条边的写法,遇到递归函数的返回值,如果不为空,立刻返回。
701.二叉搜索树中的插入操作
题目链接:701.二叉搜索树中的插入操作
文档讲解:代码随想录/二叉搜索树中的插入操作
视频讲解:视频讲解-二叉搜索树中的插入操作
状态:已完成(1遍)
解题过程
看到题目的第一想法
这道题一开始的想法是一层层递归下去,直到有空值出现,再将val赋给空值。但是很明显,为了满足搜索二叉树的特性,远比这个复杂得多。
看完代码随想录之后的想法
反倒是我想多了??我被这张图误解了
我以为会出现这样的情况,不能等到叶子结点的时候再做处理。但其实在原来的二叉树中添加5,也是完全可以在叶子结点中添加的。
看了讲解手搓代码如下:
/**
* 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} val
* @return {TreeNode}
*/
var insertIntoBST = function (root, val) {
const insert = function (node) {
if (node == null) {
let newNode = new TreeNode(val);
return newNode;
}
if (node.val > val) node.left=insert(node.left);
if (node.val < val) node.right=insert(node.right);
return node;
}
return insert(root);
};
总结
只要想通了不管给的新值是多少,都可以让它待在原搜索二叉树的叶子结点之后,这道题就容易多了。
450.删除二叉搜索树中的节点
题目链接:450.删除二叉搜索树中的节点
文档讲解:代码随想录/删除二叉搜索树中的节点
视频讲解:视频讲解-删除二叉搜索树中的节点
状态:已完成(1遍)
解题过程
看到题目的第一想法
好好好,这道题目我看才是真要改变结构了,没办法找到叶子结点再做不改动结构的操作了。真投降了。
看完代码随想录之后的想法
讲解中列举了删除节点的所有情况:
- 第一种情况:没找到删除的节点,遍历到空节点直接返回了
- 找到删除的节点
- 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
- 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
- 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
- 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
讲解代码如下:
/**
* 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;
if (key > root.val) {
root.right = deleteNode(root.right, key);
return root;
} else if (key < root.val) {
root.left = deleteNode(root.left, key);
return root;
} else {
// 场景1: 该节点是叶节点
if (!root.left && !root.right) {
return null
}
// 场景2: 有一个孩子节点不存在
if (root.left && !root.right) {
return root.left;
} else if (root.right && !root.left) {
return root.right;
}
// 场景3: 左右节点都存在
const rightNode = root.right;
// 获取最小值节点
const minNode = getMinNode(rightNode);
// 将待删除节点的值替换为最小值节点值
root.val = minNode.val;
// 删除最小值节点
root.right = deleteNode(root.right, minNode.val);
return root;
}
};
function getMinNode(root) {
while (root.left) {
root = root.left;
}
return root;
}
总结
这道题看了删除节点的所有情况之后,思路会略清晰一点。