Leetcode: 669 修剪二叉搜索树
递归法
这道题的修建比上道题删除节点简单,因为当判断出当前节点小于low的时候,就只需要向当前节点右孩子寻找,同理大于high的时候,向左孩子寻找,然后将左右孩子接上就可以了。
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root == NULL) return NULL;//终止条件
if(root->val < low) return trimBST(root->right, low, high);//寻找符合条件的右孩子
else if(root->val > high) return trimBST(root->left, low, high);
root->left = trimBST(root->left, low, high);//将符合条件的左孩子接入
root->right = trimBST(root->right, low,high);
return root;
}
};
迭代法
代码为代码随想录提供代码随想录
迭代法的总体代码比递归法要困难,要先处理头节点使得头节点符合范围,然后依次处理左右子树。
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int L, int R) {
if (!root) return nullptr;
// 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭
while (root != nullptr && (root->val < L || root->val > R)) {
if (root->val < L) root = root->right; // 小于L往右走
else root = root->left; // 大于R往左走
}
TreeNode *cur = root;
// 此时root已经在[L, R] 范围内,处理左孩子元素小于L的情况
while (cur != nullptr) {
while (cur->left && cur->left->val < L) {
cur->left = cur->left->right;
}
cur = cur->left;
}
cur = root;
// 此时root已经在[L, R] 范围内,处理右孩子大于R的情况
while (cur != nullptr) {
while (cur->right && cur->right->val > R) {
cur->right = cur->right->left;
}
cur = cur->right;
}
return root;
}
};
Leetcode: 108 将有序数组转换为二叉搜索树
本题有额外的要求是构造平和的二叉搜索树。基本思路就是寻找中间的分割点。
递归法
- 终止条件:当我们定义左闭右闭区间的时候。left>right就是终止条件
- 输入输出:输出节点,输入数组和左右区间
- 递归逻辑:每次先寻找中间节点 mid = left + (right-left)/2;,然后将中间节点当作根节点。
class Solution {
public:
TreeNode* createtree(vector<int>& nums, int left, int right){
if(left > right) return NULL;//终止条件
int mid = left + ((right - left)/2);//寻找中间节点的位置
TreeNode* root = new TreeNode(nums[mid]);//创造中间节点
root->left = createtree(nums, left, mid - 1);//向左子树搜索
root->right = createtree(nums, mid + 1, right);//向右子树搜索
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
TreeNode* root = createtree(nums, 0, nums.size() - 1);//开始条件
return root;
}
};
递归法
需要三个队列来模拟分割的过程。一个队列放遍历的节点,一个队列放左区间下标,一个队列放右区间下标。代码太过于复杂,因此不考掌握。
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
if (nums.size() == 0) return nullptr;
TreeNode* root = new TreeNode(0); // 初始根节点
queue<TreeNode*> nodeQue; // 放遍历的节点
queue<int> leftQue; // 保存左区间下标
queue<int> rightQue; // 保存右区间下标
nodeQue.push(root); // 根节点入队列
leftQue.push(0); // 0为左区间下标初始位置
rightQue.push(nums.size() - 1); // nums.size() - 1为右区间下标初始位置
while (!nodeQue.empty()) {
TreeNode* curNode = nodeQue.front();
nodeQue.pop();
int left = leftQue.front(); leftQue.pop();
int right = rightQue.front(); rightQue.pop();
int mid = left + ((right - left) / 2);
curNode->val = nums[mid]; // 将mid对应的元素给中间节点
if (left <= mid - 1) { // 处理左区间
curNode->left = new TreeNode(0);
nodeQue.push(curNode->left);
leftQue.push(left);
rightQue.push(mid - 1);
}
if (right >= mid + 1) { // 处理右区间
curNode->right = new TreeNode(0);
nodeQue.push(curNode->right);
leftQue.push(mid + 1);
rightQue.push(right);
}
}
return root;
}
};
Leetcode: 538 把二叉搜索树转换为累加树
第一次看题目的时候没有看懂题目含义,题目的含义为节点数值相加,从最大的数值开始,因此采用的是中序遍历,但是是右中左的一个顺序。
递归法
- 没有输出,输入为当前的节点
- 终止条件,遇到叶子节点的时候
- 基本逻辑,需要使用双指针法,来记录上一个节点累加的数值。
class Solution {
public:
int pre = 0;
void number(TreeNode* node){
if(node == NULL) return;
number(node->right);
node->val += pre;
pre = node->val;
number(node->left);
}
TreeNode* convertBST(TreeNode* root) {
pre = 0;
number(root);
return root;
}
};
递归法
采用之前中序遍历的统一写法,只不过顺序变换了一下。
class Solution {
private:
int pre; // 记录前一个节点的数值
void traversal(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->right; // 右
} else {
cur = st.top(); // 中
st.pop();
cur->val += pre;
pre = cur->val;
cur = cur->left; // 左
}
}
}
public:
TreeNode* convertBST(TreeNode* root) {
pre = 0;
traversal(root);
return root;
}
};
总结
非常好的总结博客。
遇见的主要类型的题目
- 二叉树的基础知识
- 二叉树的遍历方式(广度、深度)
- 二叉树的属性(深度、高度、节点、路径、数值)
- 二叉树的修改与构造(翻转、构造、合并、删除)
- 二叉树公共祖先
- 二叉搜索树的各种题目
一般遍历方式
- 涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。
- 求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算。
- 求二叉搜索树的属性,一定是中序了,要不白瞎了有序性了。
- 一般大部分题目都是采用后序的方法来实现