leetcode算法刷题的第十七天
1.leetcode 235.二叉搜索树的最近公共祖先
题目链接
第一种解法:递归法
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/class Solution {
public:TreeNode* traversal(TreeNode* current,TreeNode* p,TreeNode* q){if(current==NULL) return NULL;if(current->val>p->val&¤t->val>q->val){TreeNode* left=traversal(current->left,p,q);if(left!=NULL) return left;}if(current->val<p->val&¤t->val<q->val){TreeNode* right=traversal(current->right,p,q);if(right!=NULL) return right;}return current;}TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {return traversal(root,p,q);}
};
因为二叉搜索树的性质,当两个节点的值的大小都比根节点小的话,那说明最近公共祖先肯定是在左子树中,反之就在右子树中。至于遍历的顺序,也是因为这个性质,所以不用考虑这个遍历顺序,前中后序遍历都可以。
第二种解法:迭代法
利用其有序性,迭代的方式还是比较简单的。
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/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;}
};
思路总结:相比于二叉树的最近公共祖先,二叉搜索树的最近公共祖先还是更加简单的,究其原因就是二叉搜索树的特性,当然我们看到二叉搜索树的时候,脑子也要想到中序遍历,这样我们所组成的数组就是有序的,这样对我们的操作更加的方便。
2.leetcode 701.二叉搜索树中的插入操作
题目链接
第一种解法:递归法
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
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);}if(root->val<val){root->right=insertIntoBST(root->right,val);}return root;}
};
第二种解法:迭代法
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode* insertIntoBST(TreeNode* root, int val) {if(root==NULL){TreeNode* node=new TreeNode(val);return node;}TreeNode* current=root;TreeNode* parent=root;//这个很重要,需要记录上一个节点,否则无法赋值新节点while(current!=NULL){parent=current;if(current->val>val) current=current->left;else current=current->right;}TreeNode* node=new TreeNode(val);if(val<parent->val) parent->left=node;// 此时是用parent节点的进行赋值else parent->right=node;return root;}
};
思路总结:
首先在二叉搜索树中的插入操作,大家不用恐惧其重构搜索树,其实根本不用重构。
然后在递归中,我们重点讲了如何通过递归函数的返回值完成新加入节点和其父节点的赋值操作,并强调了搜索树的有序性。
最后依然给出了迭代的方法,迭代的方法就需要记录当前遍历节点的父节点了,这个和没有返回值的递归函数实现的代码逻辑是一样的。
3.leetcode 450.删除二叉搜索树中的节点
题目链接
这道题还是比较有难度的,因为删除节点涉及到要重构树的结构,让这个数还是二叉搜索树。
第一种解法:递归法
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {// 第一种情况:没找到删除的节点,遍历到空节点直接返回了if(root==NULL) return NULL;if(root->val==key){// 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点if(root->left==NULL&&root->right==NULL){delete root;return NULL;}// 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点else if(root->left==NULL&&root->right!=NULL){auto retNode=root->right;delete root;return retNode;}// 第四种情况:其右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点else if(root->left!=NULL&&root->right==NULL){auto retNode=root->left;delete root;return retNode;}// 第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到删除节点的右子树的最左面节点的左孩子的位置// 并返回删除节点右孩子为新的根节点。else{TreeNode* current=root->right;// 找右子树最左面的节点while(current->left!=NULL){current=current->left;}current->left=root->left;// 把要删除的节点(root)左子树放在cur的左孩子的位置TreeNode* temp=root;// 把root节点保存一下,下面来删除root=root->right;// 返回旧root的右孩子作为新rootdelete temp; // 释放节点内存return root;}}if(root->val>key) root->left=deleteNode(root->left,key);//向左遍历if(root->val<key) root->right=deleteNode(root->right,key);//向右遍历return root;}
};
第二种解法:迭代法
删除节点的迭代法还是要复杂一点的。
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
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的父节点,用来删除curwhile (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;}
};
思路总结:这道题还是建议先好好学习一下递归法,虽然迭代法和递归法的思路是一致的,但是递归法的解法更好理解。
二叉搜索树添加节点只需要在叶子上添加就可以的,不涉及到结构的调整,而删除节点操作涉及到结构的调整。
这里我们依然使用递归函数的返回值来完成把节点从二叉树中移除的操作。
这里最关键的逻辑就是第五种情况(删除一个左右孩子都不为空的节点),这种情况一定要想清楚。
而且就算想清楚了,对应的代码也未必可以写出来,所以这道题目既考察思维逻辑,也考察代码能力。
迭代法其实不太容易写出来,所以如果是初学者的话,彻底掌握第一种递归写法就够了。