题目链接:654 最大二叉树
题干:给定一个不重复的整数数组
nums
。最大二叉树可以用下面的算法从nums
递归地构建:
- 创建一个根节点,其值为
nums
中的最大值。- 递归地在最大值左边的子数组前缀上构建左子树。
- 递归地在最大值右边的子数组后缀上构建右子树。
返回nums构建的最大二叉树。
思考一:递归法。终止条件:数组长度为0则返回空,数组长度为1则返回叶子节点。单层递归逻辑:先寻找数组中最大值的下标并创建节点root,按此下标分割数组,递归处理分割后的数组作为root节点的孩子节点。
代码:
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector& nums) {
if (nums.size() == 0) return nullptr; //无元素返回null
if (nums.size() == 1) return new TreeNode(nums[0]); //一个元素返回叶子节点
int pivotpos = 0;
for (int i = 0; i < nums.size(); i++) //寻找最大值
if (nums[i] > nums[pivotpos]) pivotpos = i;
TreeNode* root = new TreeNode(nums[pivotpos]);
//分割区间
vector leftNum(nums.begin(),nums.begin() + pivotpos);
vector rightNum(nums.begin() + pivotpos + 1,nums.end());
//组装二叉树
root->left = constructMaximumBinaryTree(leftNum);
root->right = constructMaximumBinaryTree(rightNum);
return root;
}
};
思考二:为减少空间利用率,构造新函数,传参区间左右下标。
代码:
class Solution {
private:
// 在左闭右开区间[left, right),构造二叉树
TreeNode* traversal(vector& nums, int left, int right) {
if (left >= right) return nullptr;
// 分割点下标:maxValueIndex
int maxValueIndex = left;
for (int i = left + 1; i < right; ++i)
if (nums[i] > nums[maxValueIndex]) maxValueIndex = i;
TreeNode* root = new TreeNode(nums[maxValueIndex]);
// 组装二叉树
root->left = traversal(nums, left, maxValueIndex);
root->right = traversal(nums, maxValueIndex + 1, right);
return root;
}
public:
TreeNode* constructMaximumBinaryTree(vector& nums) {
return traversal(nums, 0, nums.size());
}
};
题目链接:617 合并二叉树
题干:给你两棵二叉树:
root1
和root2
。想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。合并过程必须从两个树的根节点开始。
思考一:前序遍历+递归。终止条件:两二叉树均空则返回空,若一二叉树为空则直接返回另一二叉树。单层递归逻辑:创建新节点,其val值为两二叉树节点val值之和,并处理两二叉树左右子树作为新节点的左右子树。
代码:
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1 && !root2) return nullptr;
if (root1 && !root2) return root1;
if (!root1 && root2) return root2;
TreeNode* root = new TreeNode(root1->val + root2->val); //中
root->left = mergeTrees(root1->left,root2->left); //左
root->right = mergeTrees(root1->right, root2->right); //右
return root;
}
};
思考二:前序遍历+迭代。指定修改一二叉树返回。循环中一次性将两二叉树相同位置的结点加入队列,一次性将队列中的两个结点处理。两二叉树的孩子结点均不为空则加入队列下一个循环处理。若指定返回二叉树的孩子结点为空而另一二叉树对应孩子节点不为空则更改指针即可。
代码:
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;
if (!root2) return root1;
queue que;
que.push(root1);
que.push(root2);
while (!que.empty()) {
TreeNode* cur1 = que.front(); que.pop();
TreeNode* cur2 = que.front(); que.pop();
cur1->val += cur2->val; //中
//两二叉树左子树均不为空
if (cur1->left && cur2->left) {
que.push(cur1->left);
que.push(cur2->left);
}
//两二叉树右子树均不为空
if (cur1->right && cur2->right) {
que.push(cur1->right);
que.push(cur2->right);
}
//cur1左子树为空,cur2左子树不为空
if (!cur1->left && cur2->left)
cur1->left = cur2->left;
//cur1右子树为空,cur2右子树不为空
if (!cur1->right && cur2->right)
cur1->right = cur2->right;
}
return root1;
}
};
题目链接:700 二叉搜索树中的搜索
题干:给定二叉搜索树(BST)的根节点
root
和一个整数值val
。你需要在 BST 中找到节点值等于val
的节点。返回以该节点为根的子树。如果节点不存在,则返回null
。
思考一:递归法。当前节点为空则查找失败返回空,若不为空则比较val值。若当前节点val值等于目标值则返回当前节点,若大于则递归左子树,若小于则递归右子树。
代码:
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (!root) return nullptr;
if (root->val == val) return root; //查找成功
else if (root->val > val) //当前节点值大
return searchBST(root->left, val);
else //当前节点值小
return searchBST(root->right, val);
}
};
思考二:迭代法。通常思路:使用栈来模拟深度遍历,使用队列来模拟广度遍历。而二叉搜索树存在节点的有序性,不使用辅助栈或者队列就可以写出迭代法。
代码:
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
while (root) {
if (root->val > val) return root->left;
else if (root->val < val) return root->right;
else return root;
}
return nullptr;
}
};
题目链接:98 验证二叉搜索树
题干:给你一个二叉树的根节点
root
,判断其是否是一个有效的二叉搜索树。有效二叉搜索树定义如下:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
思考一:中序递归法。关键点:中序遍历下,输出的二叉搜索树节点的数值是有序序列。
易错点:
中序递归遍历,记录左右子树递归返回结果,判断当前节点时遇到当前节点root的val值是否小于前一个节点pre的val值,是则返回false。判断逻辑结束后要修改前一个节点pre指向当前节点root
代码:
class Solution {
public:
TreeNode* pre = nullptr; //记录前一个结点
bool isValidBST(TreeNode* root) {
if (!root) return true;
bool left = isValidBST(root->left); //左
if (pre != nullptr && pre->val >= root->val) return false; //中
pre = root; //更新前一个结点
bool right = isValidBST(root->right); //右
return left && right;
}
};
思考二:迭代法。在中序遍历迭代写法的基础上简单修改循环中出栈元素后的处理逻辑:改为判断逻辑和更新记录节点
中序遍历迭代写法:第十四天| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法
代码:
class Solution {
public:
bool isValidBST(TreeNode* root) {
stack st;
TreeNode* cur = root;
TreeNode* pre = nullptr; //记录前一个节点
while(cur != nullptr || !st.empty()) {
if (cur != nullptr) {
st.push(cur);
cur = cur->left; //左 指针访问到最左下
} else {
cur = st.top(); //栈顶存放着左孩子为空或者左孩子已经访问过的节点
st.pop();
if (pre != nullptr && pre->val >= cur->val) return false; //中
pre = cur; //更新前一个节点
cur = cur->right; //右
}
}
return true;
}
};
思考三:统一迭代法。在中序遍历统一迭代写法的基础上简单修改循环中的处理逻辑:改为判断逻辑和更新记录节点
中序遍历统一迭代写法:第十四天| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法
代码:
class Solution {
public:
bool isValidBST(TreeNode* root) {
stack st;
if (root == nullptr) return true;
TreeNode* pre = nullptr; //记录前一个节点
st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
if (cur != nullptr) {
st.pop();
if (cur->right != nullptr) st.push(cur->right); //右
st.push(cur); //中
st.push(nullptr);//null标志 说明后面的节点访问过
if (cur->left != nullptr) st.push(cur->left); //左
} else { //遇到null统一输出栈中后一个元素
st.pop();
cur = st.top();
st.pop();
if (pre != nullptr && pre->val >= cur->val) return false;
pre = cur;
}
}
return true;
}
};
自我总结: