目录
题目链接:235. 二叉搜索树的最近公共祖先
思路
与普通二叉树找最近公共祖先不同的是,二叉搜索树内的节点已经有顺序了。又因为树中无重复节点,所以从上往下搜索,每次只要对比p,q与节点值的关系即可。都小于则往左搜索,都大于则往右搜索,一大一小说明找到了最近公共祖先。
代码
class Solution {
public:
// 递归法
TreeNode* traversal(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == NULL)
return root; // 空节点直接返回
// p和q都小于当前节点值,说明p和q都在当前节点的左边
if (root->val > p->val && root->val > q->val) {
TreeNode* left = traversal(root->left, p, q);
if (left != NULL)
return left;
}
// p和q都大于当前节点的值,说明p和q都在当前节点的右边
if (root->val < p->val && root->val < q->val) {
TreeNode* right = traversal(root->right, p, q);
if (right != NULL)
return right;
}
return root;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
return traversal(root, p, q);
}
};
class Solution {
public:
// 迭代法
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode* cur = root;
while (cur) {
if (cur->val > p->val && cur->val > q->val) {
cur = cur->left;
} else if (cur->val < p->val && cur->val < q->val) {
cur = cur->right;
} else
break;
}
return cur;
}
};
题目链接:701.二叉搜索树中的插入操作
思路
因为插入方式不唯一,选择将新节点插入到叶子节点,这样对树的结构改变最小,利于操作。又因为是二叉搜索树,所以只需要每次对比插入节点的值与当前节点的值的大小,然后确定遍历的方向,直到叶子节点完成插值。
代码
class Solution {
public:
// 迭代法
TreeNode* insertIntoBST(TreeNode* root, int val) {
TreeNode* newnode = new TreeNode(val); // 创建新节点
// 单独处理根节点
if (root == NULL) {
root = newnode;
return root;
}
TreeNode* cur = root;
while (cur) {
// val的值比当前节点小,应该往左子树插
if (cur->val > val) {
if (cur->left == NULL) {
// 遍历到叶子节点时,插入节点,结束
cur->left = newnode;
break;
}
cur = cur->left; // 向左遍历
}
// val的值比当前节点大,应该往右子树插
else if (cur->val < val) {
if (cur->right == NULL) {
// 遍历到叶子节点时,插入节点,结束
cur->right = newnode;
break;
}
cur = cur->right; // 向右遍历
}
}
return root;
}
};
class Solution {
public:
// 递归法
TreeNode* traversal(TreeNode* cur, int val) {
if (cur == NULL) {
// 遍历到了空节点,即找到了节点的插入位置
TreeNode* newnode = new TreeNode(val);
return newnode; // 将这个需要插入的节点返回给上一层
}
// val小于当前值,向左
if (val < cur->val) {
// 假设遍历到最后一层,找到了插入位置
// 此时倒数第二层的节点将最后一层返回上来的新节点接住当作左孩子
// 然后逐步向根节点回溯
cur->left = traversal(cur->left, val);
}
// val大于当前值,向右,同理
if (val > cur->val) {
cur->right = traversal(cur->right, val);
}
return cur; // 递归时的回溯过程,最后递归结束后返回根节点
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
return traversal(root, val);
}
};
题目链接:450.删除二叉搜索树中的节点
思路
删除节点与插入节点的操作不同的地方在于,删除节点后可能需要手动调整二叉树的结构。分为以下几种情况及处理方式
①未找到删除的节点
不进行操作
②删除的节点位于叶子节点,左空右空
让其父节点指向NULL
③删除的节点只有左孩子,左不为空右空
让其父节点指向其左孩子
④删除的节点只有右孩子,左空右不为空
让其父节点指向右孩子
⑤删除的节点左右孩子都有,左不为空右不为空
左子树的值都比当前节点小,右子树的值都比当前节点大
要删除当前节点,需要为左右子树找到各自的父节点
右子树的父节点就是当前节点的父节点
左子树的父节点是右子树中最小值的节点,也就要在右子树中一直向左遍历
代码
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == NULL) {
return NULL;
}
// 找到了要删除的节点
if (root->val == key) {
// 左右都为空
if (root->left == NULL && root->right == NULL) {
delete root; // 用来释放内存
return NULL;
}
// 左不空右空
else if (root->left != NULL && root->right == NULL) {
auto node = root->left;
delete root; // 释放内存
return node;
}
// 左空右不空
else if (root->left == NULL && root->right != NULL) {
auto node = root->right;
delete root; // 用来释放内存
return node;
}
// 左右都不空
else {
auto node = root->right; // 用来释放内存
TreeNode* cur = root->right; // 临时变量访问右子树
while (cur->left != NULL) {
cur = cur->left; // 找到右子树中的最小值
}
cur->left = root->left; // 将删除节点的左子树移到右子树下
delete root; // 内存释放
return node; // 返回右子树
}
}
if (key < root->val) {
root->left = deleteNode(root->left, key);
} else if (key > root->val) {
root->right = deleteNode(root->right, key);
}
return root;
}
};
总结
①一定灵活运用搜索二叉树的性质,自带顺序
②在二叉树章节,一直在使用递归,谨记递归三部曲
③复杂问题先进行分类讨论,细化问题,逐步处理