相对于 二叉树的最近公共祖先 本题就简单一些了,因为 可以利用二叉搜索树的特性。
题目链接/文章讲解:代码随想录
视频讲解:二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先_哔哩哔哩_bilibili
这道题明晃晃就要是要利用二叉搜索树的节点大小的特点,如果当前节点的值比这两个的都要大,那就往左边找(单独左子树递归即可),如果当前的值比这两个的值都要小那就往右边找(单独右子树递归即可),如果恰好处于中间的一个值,那就证明当前节点就是最近公共祖先,然后返回给上个节点的递归即可(这么找上个节点必然是这个节点的父母节点,所以必然比这两个数都大或小),再直接返回找到的结果即可——
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
// if(root==NULL||root==p||root==q) return root;
if(root->valval,q->val)){
return lowestCommonAncestor(root->right,p,q);
}
if(root->val>max(p->val,q->val)){
return lowestCommonAncestor(root->left,p,q);
}
return root;
}
其实,一旦遇到二叉搜索树,迭代法就会很简单,因为二叉搜索树的题它一般都是要利用它的节点大小特点,所以搜索的路径一般都是固定的。这题的思路和递归的一样——
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while(root){
if(root->val>max(p->val,q->val)) root=root->left;
else if(root->valval,q->val)) root=root->right;
else return root;
}
return NULL;
}
本题比想象中的简单,大家可以先自己想一想应该怎么做,然后看视频讲解,就发现 本题为什么比较简单了。
题目链接/文章讲解:代码随想录
视频讲解:原来这么简单? | LeetCode:701.二叉搜索树中的插入操作_哔哩哔哩_bilibili
这道题首先说可以不调整节点,就说明可以将所有加入的节点都放在叶子节点处,不调整整棵树,这样就会简单很多。
使用递归的方法,首先要明确递归何时结束,也就是当当前节点为空的时候,就意味着要返回你的新节点了(已经到达了当前树的叶子节点),如果当前不为空,就要判断当前节点的值大于还是小于加入的值,然后直接将递归调用的返回节点作为当前节点的左子树或右子树,然后最后返回整棵树,作为上一层的左子树或右子树——
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root==NULL) return new TreeNode(val);
if(root->val>val) root->left=insertIntoBST(root->left,val);
if(root->valright=insertIntoBST(root->right,val);
return root;
}
迭代法是很简单的,就是找到合适的叶子节点处,然后给这个叶子节点加一个子节点即可——
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root==NULL) return new TreeNode(val);
TreeNode* cur=root;
TreeNode* pre=NULL;
while(cur){
pre=cur;
if(cur->val>val) cur=cur->left;
else cur=cur->right;
}
cur=new TreeNode(val);
if(pre->val>val) pre->left=cur;
else pre->right=cur;
return root;
}
相对于 插入操作,本题就有难度了,涉及到改树的结构
题目链接/文章讲解:代码随想录
视频讲解:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点_哔哩哔哩_bilibili
这道题其实主要是要搞懂怎么删除节点,要删除的节点分成三种情况:
TreeNode* aided(TreeNode* node){
if(node->left==NULL&&node->right==NULL) {
delete(node);
return NULL;
}
TreeNode* tmp=node;
if(node->left&&node->right==NULL){
tmp=tmp->left;
delete(node);
return tmp;
}
if(node->right&&node->left==NULL){
tmp=tmp->right;
delete(node);
return tmp;
}
tmp=tmp->right;
while(tmp->left){
tmp=tmp->left;
}
tmp->left=node->left;
tmp=node->right;
delete(node);
return tmp;
}
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root) return NULL;
if(root->val==key){
return aided(root);
}else if(root->val>key) root->left=deleteNode(root->left,key);
else root->right=deleteNode(root->right,key);
return root;
}
文章还给出了删除普通二叉树的方法,就是当要删除的节点本身没有右孩子的时候,直接返回左孩子,如果有右孩子,那就将右孩子的最左边的孩子的值和当前节点的值交换一下,一直交换到目标节点的右孩子为空为止,这样再直接返回目标节点的左孩子。这样做本质上是没有对要删除的节点进行彻底的空间释放——
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == nullptr) return root;
if (root->val == key) {
if (root->right == nullptr) { // 这里第二次操作目标值:最终删除的作用
return root->left;
}
TreeNode *cur = root->right;
while (cur->left) {
cur = cur->left;
}
swap(root->val, cur->val); // 这里第一次操作目标值:交换目标值其右子树最左面节点。
}
root->left = deleteNode(root->left, key);
root->right = deleteNode(root->right, key);
return root;
}
迭代的做法。首先要找到要删除的目标节点,然后再把删除节点之后的子树移接到它的父母节点,如果没有父母节点,就直接返回删除后的子树。删除节点主要考虑到三种情况:
class Solution {
private:
// 将目标节点(删除节点)的左子树放到 目标节点的右子树的最左面节点的左孩子位置上
// 并返回目标节点右孩子为新的根节点
// 是动画里模拟的过程
TreeNode* deleteOneNode(TreeNode* target) {
if (target == nullptr) return target;
if (target->right == nullptr) return target->left;
TreeNode* cur = target->right;
while (cur->left) {
cur = cur->left;
}
cur->left = target->left;
return target->right;
}
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == nullptr) return root;
TreeNode* cur = root;
TreeNode* pre = nullptr; // 记录cur的父节点,用来删除cur
while (cur) {
if (cur->val == key) break;
pre = cur;
if (cur->val > key) cur = cur->left;
else cur = cur->right;
}
if (pre == nullptr) { // 如果搜索树只有头结点
return deleteOneNode(cur);
}
// pre 要知道是删左孩子还是右孩子
if (pre->left && pre->left->val == key) {
pre->left = deleteOneNode(cur);
}
if (pre->right && pre->right->val == key) {
pre->right = deleteOneNode(cur);
}
return root;
}
};