235 二叉搜索树的最近公共祖先
用236 普通二叉树(没顺序的)代码也可以过,但是本题还是要利用特性:搜索二叉树有序
关键:
如果一个节点的值在 p 和 q 之间(即 p < cur < q 或者 q < cur < p),那么这个节点就是 p 和 q 的最近公共祖先。 我觉得甚至不用随想录说的“第一次遇到 cur节点是数值在[p, q]区间中,即 节点5,此时可以说明 p 和 q 一定分别存在于 节点 5的左子树,和右子树中” 第一次,就是只要满足就是了。不过他的意思应该是找到就行。如果数值在pq之间就一定是最近的了,因为再远的话,就pq都在一个子树里面了。
我写的↓,我处理null确实和他gpt写的不一样
TreeNode* traverse(TreeNode *node, int large, int small) {
if (node->val > small && node->val < large) return node;
else if (node->val > large && node->left)
return traverse(node->left, large, small);
else if (node->val < small && node->right)
return traverse(node->right, large, small);
return node;
}
TreeNode* lowestCommonAncestor(TreeNode* node, TreeNode* p, TreeNode* q) {
int large = std::max(p->val, q->val);
int small = std::min(p->val, q->val);
if(node==nullptr) return nullptr;
return traverse(node, large, small);
}
gpt写的:
TreeNode* traverse(TreeNode *node, int large, int small) {
if (node == nullptr) return nullptr;
if (node->val > small && node->val < large) {
return node;
} else if (node->val > large) {
return traverse(node->left, large, small);
} else if (node->val < small) {
return traverse(node->right, large, small);
} else {
return node;
}
}
TreeNode* lowestCommonAncestor(TreeNode* node, TreeNode* p, TreeNode* q) {
int large = std::max(p->val, q->val);
int small = std::min(p->val, q->val);
return traverse(node, large, small);
}
随想录精简版真的够精简的:
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;
}
701.二叉搜索树中的插入操作
也有复杂的,要改变结构的插入,但是可以不做那个。可以不考虑题目中提示所说的改变树的结构的插入方式,只是找到符合大小的空节点就行。
自己写的递归,但我觉得写的不好,是到处都在打补丁的代码。现在真的几乎所有题都是写的递归,感觉迭代抽空也要练练了
TreeNode* prev=nullptr;
TreeNode * newnode=nullptr;
void traverse(TreeNode *node, int val){
if(node==nullptr) return;
if(node->left) traverse(node->left,val);
if(prev==nullptr && node->val>val){
newnode= new TreeNode(val);
node->left=newnode;
}
if(prev && prev->val<val && node->val >val){
newnode= new TreeNode(val);
if(node->left==nullptr) node->left=newnode;
else if(prev->right==nullptr) prev->right=newnode;
}
prev=node;//?
if(node->right) traverse(node->right,val);
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
traverse(root, val);
if(root==nullptr) root=new TreeNode(val);
else if(newnode==nullptr && prev && prev->val<val){
newnode= new TreeNode(val);
prev->right=newnode;
}
return root;
}
看随想录写的,此题递归func用 void 或者 有return返回值的都可以,有return返回值的明显看起来简洁多了。根据随想录的void把我的void 递归修改一下,原来有以下几个问题:
1.再次强调二叉搜索数遍历不是左中右,要用val大小和node val判断。递归func既然一开始写了if(node==nullptr) return;,去左右孩子就不要考虑if(node->left)这么写了。
2. 我的逻辑乱七八糟的,一开始只考虑了值在prev 和node值中间的情况,后来还为小于最小的,大于最大的打补丁。 其实清晰逻辑很简单:就按照搜索二叉的顺序,大了去右边,小了去左边,那找到第一个空的地方就是该插入的。下面是随想录的递归func是void的
class Solution {
private:
TreeNode* parent;
void traversal(TreeNode* cur, int val) {
if (cur == NULL) {
TreeNode* node = new TreeNode(val);
if (val > parent->val) parent->right = node;
else parent->left = node;
return;
}
parent = cur;
if (cur->val > val) traversal(cur->left, val);
if (cur->val < val) traversal(cur->right, val);
return;
}
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
parent = new TreeNode(0);
if (root == NULL) {
root = new TreeNode(val);
}
traversal(root, val);
return root;
}
};
下面是随想录的,递归函数有返回值的
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);
if (root->val < val) root->right = insertIntoBST(root->right, val);
return root;
}
迭代本质是一样的,也是需要记录一个prev。随想录:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == NULL) {
TreeNode* node = new TreeNode(val);
return node;
}
TreeNode* cur = root;
TreeNode* parent = root; // 这个很重要,需要记录上一个节点,否则无法赋值新节点
while (cur != NULL) {
parent = cur;
if (cur->val > val) cur = cur->left;
else cur = cur->right;
}
TreeNode* node = new TreeNode(val);
if (val < parent->val) parent->left = node;// 此时是用parent节点的进行赋值
else parent->right = node;
return root;
}
450 删除二叉搜索树中的节点
这题比较难,但是居然自己做出来了,递归逻辑和随想录的一样,没差
但是看了随想录关于需删node左右孩子都有的情况:一开始自己想的涉及旋转什么,特别乱。看了随想录发现,其实就是:把左subtree放入右subtree最左的node的左孩子处,然后让右subtree的root当新node。想通了真的非常简单。
需要注意:1.没做内存管理,delete node,在gpt帮助下改了(其实我自己不会写
TreeNode* toDelete = node; // 保存要删除的节点的指针;
delete toDelete; // 释放要删除的节点
2. 搜索二叉树的遍历顺序不是前中后这样算的,因为是根据 大于等于小于val这样算的,是并列的if else条件,所以顺序怎么样写都行
void traverse(TreeNode * &node,int key){
if(node==nullptr) return;
if(node->val > key && node->left)
traverse(node->left, key);
else if(node->val < key && node->right)
traverse(node->right, key);
else if(node->val==key){
TreeNode* toDelete = node; // 保存要删除的节点的指针
if(node->left==nullptr && node->right==nullptr)
node=nullptr;
else if(node->left && node->right==nullptr)
node=node->left;
else if(node->right && node->left==nullptr)
node=node->right;
else {
TreeNode* leftmost=node->right;
while(leftmost->left){
leftmost=leftmost->left;
}
leftmost->left=node->left;
node=node->right;
}
delete toDelete; // 释放要删除的节点的内存
}
}
TreeNode* deleteNode(TreeNode* root, int key) {
traverse(root, 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;
}
};
普通二叉树删除节点
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;
}