题目1:236.二叉树的最近公共祖先

首先要了解什么是祖先:如果节点p在节点root的左(右)子树中,或者p=root(即节点的祖先可以是自己),则root为p的祖先。

最近公共祖先:对于有根树的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
在Krahets大佬的题解中给了一种其它解释的最近公共祖先的定义:设节点root为节点 p、q 的某公共祖先,若其左子节点root->left 和右子节点root->right 都不是p、q 的公共祖先,则称root是 “最近的公共祖先” 。
那么根据定义就有三种情况:
1.节点p在节点root的左子树中,节点q在节点root的右子树中,那么节点root就是p和q的最近公共祖先;
2.节点p=节点root,节点q在root的左(右)子树中;
3.节点q=节点root,节点p在root的左(右)子树中;
至于题目中要求的深度尽可能大,可以通过后序遍历,从低向上遍历找到的即为最近公共祖先。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == q || root == p || root == NULL) return root; //找到了p或q、或遇到了空节点
TreeNode* left = lowestCommonAncestor(root->left, p, q); //递归左子树
TreeNode* right = lowestCommonAncestor(root->right, p, q); //递归右子树
if (left != NULL && right != NULL) return root; //p,、q分别在root的异侧(左\右子树),root为最近公共祖先
if (left == NULL && right != NULL) return right; //p、q都不在root的左子树中,有两种情况:1.p、q其中一个在root的右子树中,此时right指向p(或q) 2.p、q都在root的右子树中,此时right指向最近公共祖先节点
else if (left != NULL && right == NULL) return left; //p、q都不在root的右子树中,两种情况同理
else return NULL; //left == NULL && right == NULL 左右子树都不包含p、q
}
};
代码写的很巧妙,尤其是返回值left和right的几种情况,可以打上断点自己调试看看运行过程
题目2:235.二叉搜索树的最近公共祖先

提到二叉搜索树那肯定就要用它的特点:左子树的所有节点都小于当前节点,右子树的所有节点都大于当前节点。
在遍历过程中:
如果两个节点p、q都小于根节点,说明p、q在根节点的左子树上;
如果两个节点p、q都大于根节点,说明p、q在根节点的右子树上;
如果节点p大于根节点、节点q小于根节点,说明p在根节点的左子树上、q在根节点的右子树上,此时根节点就是p、q的最近公共祖先节点。
为什么某个节点使p、q位于左右子树,那么这个节点就是p、q的最近公共祖先:
可以这么理解,从根节点开始寻找p、q,比如寻找3和5,它们的路径(之前路径使一样的6->2->4)在节点4分开,节点4也就是它们最后一个相同的节点,根据定义也就是最近的公共祖先。

解法一:递归法
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root->val > p->val && root->val > q->val) {
return lowestCommonAncestor(root->left, p, q);
}
else if (root->val < p->val && root->val < q->val) {
return lowestCommonAncestor(root->right, p, q);
}
else return root; //p->val <= root->val && root->val <= q->val 或者q->val <= root->val && root->val <= p->val
}
};
解法二:迭代法
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while(root) {
if (root->val > p->val && root->val > q->val) {
root = root->left;
} else if (root->val < p->val && root->val < q->val) {
root = root->right;
} else return root;
}
return NULL;
}
};
题目3:701.二叉搜索树中的插入操作


按照搜索二叉树的遍历特点去插入元素:
如果根节点的值小于val,则将根节点的右子树结点作为根节点继续搜索;
如果根节点的值大于val,则将根节点的左子树的结点作为根节点继续搜索;
当遇到结点为空时,此时返回一个新结点,进行插入操作。

解法一:递归法
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == NULL) {
TreeNode* node = new TreeNode(val);
return node;
}
if (root->val > val) root->left = insertIntoBST(root->left, val); //val比root值大,对右子树节点进行递归操作
if (root->val < val) root->right = insertIntoBST(root->right, val); //val比root值小,对左子树节点进行递归操作
return root; //向上层返回已经完成插入操作的节点
}
};
解法二:迭代法
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == NULL) {
TreeNode* node = new TreeNode(val);
return node;
}
TreeNode* cur = root; //记录当前节点
TreeNode* pre = root; //记录上一个节点
while (cur != NULL) {
pre = cur;
if (cur->val > val) cur = cur->left; //val比root值大
else cur = cur->right; //val比root值小
}
TreeNode* node = new TreeNode(val);
if (val < pre->val) pre->left = node; //用parent节点的进行赋值
else pre->right = node;
return root;
}
};
题目4:450.删除二叉搜索树中的节点

删除节点的两个步骤,首先是找到要删除的节点,这一步和701的搜索操作时一样的,根据搜索二叉树的特点去遍历,找到要删除的节点。
在搜索二叉树中删除节点有以下几种情况:
1.遍历到空节点,说明没找到要删除的节点,直接返回;
2.如果要删除的节点左子树为空、右子树不为空,则删除节点,右子树顶替其位置;
3.如果要删除的节点左子树不为空、右子树为空,则删除节点,左子树顶替其位置;
4.如果要删除的节点左右子树都为空(即为叶子节点),直接删除节点;
5.如果要删除的节点左右子树都不为空,将要删除节点的左子树转移到其右子树的最左节点的左子树上,然后右子树顶替其位置,删除了该节点。

解法一:迭代法
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == NULL) return root; //遍历到空节点,说明没找到要删除的节点,直接返回
if (root->val > key) root->left = deleteNode(root->left, key); //根节点的值大于key,搜索左子树
if (root->val < key) root->right = deleteNode(root->right, key); //根节点的值小于key,搜索右子树
if (root->val == key) { //当前节点就是要删除的节点
if (root->left == NULL && root->right == NULL) { //要删除的节点左右子树都为空(即为叶子节点),直接删除节点
delete root; //释放内存
return NULL;
}
else if (root->left == NULL && root->right != NULL) { //要删除的节点左子树为空、右子树不为空
TreeNode* node = root->right; //删除节点,右子树顶替其位置
delete root;
return node;
}
else if (root->left != NULL && root->right == NULL) { //要删除的节点左子树不为空、右子树为空
TreeNode* node = root->left; //删除节点,左子树顶替其位置
delete root;
return node;
}
else { //要删除的节点左右子树都不为空,将要删除节点的左子树转移到其右子树的最左节点的左子树上
TreeNode* cur = root->right; //要删除的节点的右子树
while (cur->left != NULL) { //找右子树最左边的节点
cur = cur->left;
}
cur->left = root->left; //把要删除的节点(root)左子树放在cur的左子树的位置
root = root->right; //返回旧root的右子树作为新root
return root;
}
}
return root;
}
};
代码优化后:
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key)
{
if (root == nullptr) return nullptr; //遍历到空节点
if (root->val < key) root->right = deleteNode(root->right, key); //根节点的值小于key,搜索右子树
else if (root->val > key) root->left = deleteNode(root->left, key); //根节点的值大于key,搜索左子树
else //当前节点就是要删除的节点
{
if (!root->left) return root->right; //要删除的节点左子树为空、右子树不为空
if (!root->right) return root->left; //要删除的节点左子树不为空、右子树为空
TreeNode* node = root->right; //要删除的节点左右子树都不为空
while (node->left) { //寻找要删除节点右子树的最左节点
node = node->left;
}
node->left = root->left; //将要删除节点的左子树成为其右子树的最左节点的左子树
root = root->right; //要删除节点的右子顶替其位置,节点被删除
}
return root;
}
};
这篇博客介绍了二叉搜索树的基本操作,包括最近公共祖先的查找,插入新节点以及删除节点的方法。对于最近公共祖先,通过后序遍历或根据节点值比较可以找到;插入节点时,遵循搜索二叉树的性质递归插入;删除节点则需要考虑不同情况,如节点的子树为空或不为空。这些操作都是基于二叉搜索树的特性进行的高效算法实现。
1215

被折叠的 条评论
为什么被折叠?



