二叉树:以为使用了递归,其实还隐藏着回溯
100. 相同的树
bool compare(TreeNode* left, TreeNode* right) {
// 情况1:叶子节点
if(!left && !right) return true;
// 情况2:左右节点值相等,继续向下比较(递归)
else if(left && right && left->val==right->val)
return compare(left->left, right->left)
&& compare(left->right, right->right);
else return false;
}
bool isSameTree(TreeNode* p, TreeNode* q) {
return compare(p,q);
}
257. 二叉树的所有路径
- 显式回溯版:
void traversal(TreeNode* cur, vector<int>& path, vector<string>& result){
path.push_back(cur->val);
// 叶子节点
if(!cur->left && !cur->right){
string s; int flag=0;
for(int i=0;i<path.size();i++){
if(flag) s+="->";
s+=to_string(path[i]);
flag=1;
}
result.push_back(s); return;
}
if(cur->left){
traversal(cur->left, path, result);
path.pop_back(); //回溯
}
if(cur->right){
traversal(cur->right, path, result);
path.pop_back(); //回溯
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<int> path;
vector<string> result;
if(!root) return result;
traversal(root, path, result);
return result;
}
- 隐式回溯版:
void traversal(TreeNode* cur, string path,
vector<string>& result) {
path += to_string(cur->val); // 中
if(!cur->left && !cur->right) {
result.push_back(path);
return;
}
if(cur->left) traversal(cur->left, path + "->", result); // 左
if(cur->right) traversal(cur->right, path + "->", result); // 右
}
vector<string> binaryTreePaths(TreeNode* root) {
string path;
vector<string> result;
if(!root) return result;
traversal(root, path, result);
return result;
}
- 主要需要理解:回溯隐藏在traversal(cur->left, path + "->", result);中的 path + "->"。 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。
左叶子之和
- 力扣题目链接
- 主要看分解版代码,没看懂不要看精简版!
int sumOfLeftLeaves(TreeNode* root) {
if(!root) return 0;
if(!root->left && !root->right) return 0;
// 左子树左叶子
int leftVal=sumOfLeftLeaves(root->left);
if(root->left && !root->left->left && !root->left->right)
leftVal=root->left->val;
// 右子树左叶子(这里体现不对称性)
int rightVal=sumOfLeftLeaves(root->right);
return leftVal+rightVal;
}
找树左下角的值
- 力扣题目链接
- 层序遍历直接套模板
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> q;
if(!root) q.push(root);
int result=0;
while(!q.empty()){
int size=q.size();
for(int i=0; i<size; i++){
TreeNode* node=q.front();
q.pop();
if(i==0) result=node->val;
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
}
return result;
}
- 递归隐含回溯版(为什么可以?因为前序遍历,先更新的是最左边的深度)
int maxdep=-1, result;
void traversal(TreeNode* root, int depth){
if(!root->left && !root->right){
if(depth>maxdep){
maxdep=depth;
result=root->val;
} return;
}
if(root->left) traversal(root->left, depth+1);
if(root->right) traversal(root->right, depth+1);
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
路径总和
- 一般情况下:如果需要搜索整棵二叉树,那么递归函数就不要返回值,如果要搜索其中一条符合条件的路径,递归函数就需要返回值,因为遇到符合条件的路径了就要及时返回。
bool traversal(TreeNode* node, int count){
if(!node->left && !node->right){ //叶节点
if(count==0) return true;
else return false;
}
if(node->left){
count-=node->left->val;
if(traversal(node->left,count)) return true;
count+=node->left->val; //回溯
}
if(node->right){
count-=node->right->val;
if(traversal(node->right,count)) return true;
count+=node->right->val; //回溯
}
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root) return false;
// 注意这里的逻辑,先减去根节点值的targetSum
return traversal(root, targetSum-root->val);
}
- 之前讲过的相关题目:222. 完全二叉树,110. 平衡二叉树
构造二叉树
-
构造二叉树有三个注意的点:
- 分割时候,坚持区间不变量原则,左闭右开,或者左闭又闭。
- 分割的时候,注意 后序 或者 前序 已经有一个节点作为中间节点了,不能继续使用了。
- 如何使用切割后的后序数组来切合中序数组?利用中序数组大小一定是和后序数组的大小相同这一特点来进行切割。
class Solution {
private:
// 中序区间:[inorderBegin, inorderEnd)
// 后序区间:[postorderBegin, postorderEnd)
TreeNode* traversal(vector<int>& inorder, int inorderBegin,
int inorderEnd, vector<int>& postorder,
int postorderBegin, int postorderEnd) {
if (postorderBegin == postorderEnd) return nullptr;
int rootValue = postorder[postorderEnd - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorderEnd - postorderBegin == 1) return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin;
delimiterIndex < inorderEnd; delimiterIndex++)
if (inorder[delimiterIndex] == rootValue) break;
// 切割中序数组
// 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd)
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
// 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd)
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
// 切割后序数组
// 左后序区间,左闭右开
int leftPostorderBegin = postorderBegin;
int leftPostorderEnd = postorderBegin
+ delimiterIndex - inorderBegin;
// 右后序区间,左闭右开
int rightPostorderBegin = postorderBegin
+ (delimiterIndex - inorderBegin);
int rightPostorderEnd=postorderEnd-1; // 排除最后一个元素
root->left = traversal(inorder, leftInorderBegin,
leftInorderEnd, postorder,
leftPostorderBegin,
leftPostorderEnd);
root->right = traversal(inorder, rightInorderBegin,
rightInorderEnd, postorder,
rightPostorderBegin,
rightPostorderEnd);
return root;
}
public:
TreeNode* buildTree(vector<int>& inorder,
vector<int>& postorder) {
if (inorder.empty()) return nullptr;
// 左闭右开的原则
return traversal(inorder, 0, inorder.size(),
postorder, 0, postorder.size());
}
};
- 105.从前序与中序遍历序列构造二叉树:不再赘述
最大二叉树
class Solution {
private:
TreeNode* traversal(vector<int>& nums, int left, int right){
if(left==right) return nullptr;
int idx=left, max_num=nums[left];
for(int i=left+1; i<right; i++)
if(nums[i]>max_num) {idx=i; max_num=nums[i];}
int rootVal=max_num;
TreeNode* root=new TreeNode(rootVal);
if(right-left==1) return root;
// 分割左数组
root->left=traversal(nums, left, idx);
// 分割右数组
root->right=traversal(nums, idx+1, right);
return root;
}
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
if(nums.empty()) return nullptr;
return traversal(nums, 0, nums.size());
}
};
参考资料
[1] 代码随想录
[2] Leetcode题解