leetcode hot100 二叉树(二)
书接上回:https://blog.csdn.net/weixin_74129837/article/details/148367615?spm=1001.2014.3001.5501
8.验证二叉搜索树
维护一个min_val和max_val,限制当前结点的合法值范围。min_val和max_val动态变化。
class Solution {
public:bool check(TreeNode* node,long min_val,long max_val){if(!node) return true;if(node->val<=min_val||node->val>=max_val) return false; //注意定义,等于也不行return check(node->left,min_val,node->val)&&check(node->right,node->val,max_val);}bool isValidBST(TreeNode* root) {return check(root,LONG_MIN,LONG_MAX);}
};
9.二叉搜索树中第k小的元素
在BST中,中序遍历(左-根-右)会按升序访问所有节点。
因此,第k个被访问的节点就是第k小的元素。
代码本质上是迭代实现的中序遍历,并通过提前终止(剪枝)来优化。
算法流程:向左深入->回溯到父结点->转向右子树。
class Solution {
public:int kthSmallest(TreeNode* root, int k) {stack<TreeNode*> stk;while(root||stk.size()){while(root){stk.push(root); root=root->left;} root=stk.top(); //取出最后遍历到的父结点(相当于回溯)stk.pop();if(--k==0) return root->val;root=root->right;}return root->val;}
};
10.二叉树的右视图
dfs遍历,且优先遍历右子树。
如果层数==res大小,说明当前层还未加入结点,即本次递归到的结点是当前层右数第一个结点。
class Solution {
public:void dfs(TreeNode* root,vector<int>& res,int u){if(!root) return ;//在搜索过程中总是先访问右子树//对于每一层来说,这层见到的第一个结点一定是最右边的结点//u==res.size()时,当前层级u还没有被记录到res中(res 的索引是0到u-1,共u层)if(u==res.size()) res.push_back(root->val);dfs(root->right,res,u+1);dfs(root->left,res,u+1); }vector<int> rightSideView(TreeNode* root) {vector<int> res;if(!root) return res;dfs(root,res,0);return res;}
};
11.二叉树展开为链表
核心:展开的单链表与二叉树先序遍历的顺序相同。
class Solution {
public:void flatten(TreeNode* root) {vector<TreeNode*> nodes; // 用于存储先序遍历的结果preorderTraversal(root,nodes); // 获取先序遍历结果int n=nodes.size();for (int i=1;i<n;i++){TreeNode* prev=nodes[i-1];TreeNode* curr=nodes[i];prev->left=nullptr; // 左子指针置空prev->right=curr; // 右子指针指向下个节点}}//先序遍历:根->左->右void preorderTraversal(TreeNode* root,vector<TreeNode*> &l){if(root){l.push_back(root);preorderTraversal(root->left,l);preorderTraversal(root->right,l);}}
};
12.从前序与中序遍历序列构造二叉树
class Solution {
private:unordered_map<int,int> index;
public:TreeNode* build(vector<int>& pre,vector<int>& in,int pre_left,int pre_right,int in_left,int in_right){if(pre_left>pre_right) return nullptr;int pre_root=pre_left; //pre_root是索引int in_root=index[pre[pre_root]];int left_size=in_root-in_left;TreeNode* root=new TreeNode(pre[pre_root]);root->left=build(pre,in,pre_left+1,pre_left+left_size,in_left,in_root-1);root->right=build(pre,in,pre_left+left_size+1,pre_right,in_root+1,in_right);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int n=preorder.size();for(int i=0;i<n;i++) index[inorder[i]]=i;return build(preorder,inorder,0,n-1,0,n-1);}
};
13.路径总和
如图,先利用先序遍历序列确定根节点,再利用中序遍历序列递归子树,关键是递归边界的确定。
class Solution {
public:int dfs(TreeNode* node,long sum,int target){if(!node) return 0;int cnt=0;if(sum+node->val==target) cnt++;cnt+=dfs(node->left,sum+node->val,target);cnt+=dfs(node->right,sum+node->val,target);return cnt;} int pathSum(TreeNode* root, int targetSum) {if(!root) return 0;int cnt=dfs(root,0,targetSum);cnt+=pathSum(root->left,targetSum);cnt+=pathSum(root->right,targetSum);return cnt;}
};
14.二叉树的最近公共祖先
class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if(!root) return nullptr;if(p==root||q==root) return root;TreeNode* left=lowestCommonAncestor(root->left,p,q);TreeNode* right=lowestCommonAncestor(root->right,p,q);if(right&&left) return root; //左右节点都有,最近公共祖先是根结点if(left) return left; //只有左结点if(right) return right; //只有右结点return nullptr;}
};
15.二叉树的最大路径和
- 后序遍历:采用DFS的后序遍历方式(左右根)来遍历整棵树。
- 计算单边最大增益:对于每个节点,计算其左子树和右子树能提供的最大增益(如果增益为负则取0,表示不选择该子树)。
- 更新全局最大值:对于当前节点,计算包含该节点及其左右子树的最大路径和(即
node->val + left_gain + right_gain
),并更新全局最大值max_sum
。 - 返回单边最大路径:向上返回时只能选择左或右子树中增益较大的一边加上当前节点的值。
class Solution {
public:int dfs(TreeNode* node,int& max_sum){if(!node) return 0;int left_gain=max(dfs(node->left,max_sum),0);int right_gain=max(dfs(node->right,max_sum),0);int curr_sum=node->val+left_gain+right_gain;max_sum=max(max_sum,curr_sum);return node->val+max(left_gain,right_gain);}int maxPathSum(TreeNode* root) {int max_sum=INT_MIN;dfs(root,max_sum);return max_sum;}
};