Leetcode 654.最大二叉树
题目链接:654 最大二叉树
题干:给定一个不重复的整数数组nums
。最大二叉树可以用下面的算法从nums
递归地构建:
- 创建一个根节点,其值为
nums
中的最大值。 - 递归地在最大值左边的子数组前缀上构建左子树。
- 递归地在最大值右边的子数组后缀上构建右子树。
返回nums构建的最大二叉树。
思考一:递归法。终止条件:数组长度为0则返回空,数组长度为1则返回叶子节点。单层递归逻辑:先寻找数组中最大值的下标并创建节点root,按此下标分割数组,递归处理分割后的数组作为root节点的孩子节点。
代码:
class Solution {
public:TreeNode* constructMaximumBinaryTree(vector<int>& nums) {if (nums.size() == 0) return nullptr; //无元素返回nullif (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<int> leftNum(nums.begin(),nums.begin() + pivotpos);vector<int> 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<int>& nums, int left, int right) {if (left >= right) return nullptr;// 分割点下标:maxValueIndexint 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<int>& nums) {return traversal(nums, 0, nums.size());}
};
Leetcode 617.合并二叉树
题目链接: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<TreeNode*> 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;}
};
Leetcode 700.二叉搜索树中的搜索
题目链接: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;}
};
Leetcode 98.验证二叉搜索树
题目链接:98 验证二叉搜索树
题干:给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。
有效二叉搜索树定义如下:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
思考一:中序递归法。关键点:中序遍历下,输出的二叉搜索树节点的数值是有序序列。
易错点:
- 不能单纯的比较左节点小于中间节点,右节点大于中间节点。要比较的是左子树所有节点小于中间节点,右子树所有节点大于中间节点。
- 样例中最小节点可能是int的最小值,如果这样使用最小的int做初始值来比较也是不行的,最佳方式:记录前一个遍历的节点,初始值设为null,判断逻辑也除去null情况
中序递归遍历,记录左右子树递归返回结果,判断当前节点时遇到当前节点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<TreeNode*> 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<TreeNode*> 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;}
};
自我总结:
- 理解二叉搜索树的节点有序性在代码中的体现,不涉及验证二叉搜索树性质的遍历时无需除栈和队列的辅助
- 再次复习二叉树三种遍历方式的迭代法和统一迭代法,回忆起循环逻辑。