二叉树刷题 (2)

左子叶之和 404

左叶子的定义:某节点的左节点不为空且该节点的左节点无左孩子右孩子

使用前序遍历。
递归三部曲:
1.确定返回值及函数参数:

求一棵树的左节点之和,输入根节点,返回int  

2.确定中止条件:

if(root == nullptr) return 0; 

3.确定单层递归逻辑:

int leftValue = sumOfLeftLeaves(root->left);    // 左
if (root->left && !root->left->left && !root->left->right) {
leftValue = root->left->val;
}
int rightValue = sumOfLeftLeaves(root->right);  // 右

int sum = leftValue + rightValue;               // 中
return sum;

整体C++代码:

class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
    if (root == NULL) return 0;
    if (root->left == NULL && root->right== NULL) return 0;

    int leftValue = sumOfLeftLeaves(root->left);    // 左
    if (root->left && !root->left->left && !root->left->right) { // 左子树就是一个左叶子的情况
        leftValue = root->left->val;
    }
    int rightValue = sumOfLeftLeaves(root->right);  // 右

    int sum = leftValue + rightValue;               // 中
    return sum;
}
};

找树左下角的值 513

思路:先找到深度最大再赋值,本题使用前序遍历,最先找到最左边的值。定义一个int变量depth来记录当前遍历的深度,定义一个int变量
result来记录结果

递归三部曲:

1.确定返回值及参数:

给定二叉树求符合条件的节点值,所以传入根节点,传入一个int参数 depthzz来表示当前遍历的深度,返回节点类型int 
void travesal(TreeNode* root, int depthzz);

2.确定返回值:
if (root->left == NULL && root->right == NULL) {
if (depth > maxDepth) {
maxDepth = depth; // 更新最大深度
result = root->val; // 最大深度最左面的数值
}
return;
}

3.确定单层递归逻辑:

if(result != nullptr){
	depthzz++;
	travesal(root->left,depthzz);
	depthzz--; //回溯
}
if(result != nullptr){
    depthzz ++;
 	travesal(root->right,depthzz);
	depthzz--;//回溯  
}

整体C++代码:

class Solution {
public:
int depth = 0;
int result = 0;
void travesal(TreeNode* root, int depthzz){
    if(root->left == NULL && root->right == NULL){
	if (depth > maxDepth) {
    maxDepth = depth;           // 更新最大深度
    result = root->val;   // 最大深度最左面的数值
	}
 	return;
	}

    if(root->left != nullptr){
        depthzz++;
        travesal(root->left,depthzz);
        depthzz--;
    }
    if(root->right != nullptr){
        depthzz++;
        travesal(root->right,depthzz);
        depthzz--;
    }
}
int findBottomLeftValue(TreeNode* root) {
    travesal(root,1);
    return result;
}
};  

路径总和 112

递归函数什么时候需要返回值,什么时候不需要返回值,特别有的时候返回类型为bool类型,总结如下:

  • 需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。
  • 需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。
  • 不需要搜索整棵二叉树(如找到一条符合的路径)那么递归一定需要返回值,因为遇到符合条件的路径就要及时返回。

思路:

1.确定返回参数及传入参数

本题不需要遍历整棵二叉树,所以递归有返回值,用bool表示。 

bool travesal(TreeNode* cur,int count);

2.确定中止条件:

if(cur->left == nullptr && cur->right == nullptr && count == 0) return true;
if(cur->left == nullptr && cur->right == nullptr) return false;

3.确定递归单层逻辑:

if(cur->left != nullptr){
	if(travesal(cur->left,count->cur->left->val)) return true;//含回溯逻辑
}
if(cur->right != nullptr){
	if(traveal(cur->right,count - cur->right->val)) return true;
}

return false;

整体C++代码:

class Solution {
private:
bool traversal(TreeNode* cur, int count) {
    if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
    if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回

    if (cur->left) { // 左
        count -= cur->left->val; // 递归,处理节点;
        if (traversal(cur->left, count)) return true;
        count += cur->left->val; // 回溯,撤销处理结果
    }
    if (cur->right) { // 右
        count -= cur->right->val; // 递归,处理节点;
        if (traversal(cur->right, count)) return true;
        count += cur->right->val; // 回溯,撤销处理结果
    }
    return false;
}

public:
bool hasPathSum(TreeNode* root, int sum) {
    if (root == NULL) return false;
    return traversal(root, sum - root->val);
}
};

二叉树的路径综合ii 113

思路:
本题需要搜索整棵二叉树,不用处理递归返回值,所以没有返回参数

1.确定函数返回值及参数

void travesal(TreeNode* root,int target, vector<int> res);

2.确定中止条件

if(root->left == nullptr && root->right == nullptr){
	ans.push_back(res);
	return;
}

3.确定递归单层逻辑:

if(root->left != nullptr){
	res.push_back(root->val);
	travesal(root->left,target - root->left->val,res);
	res.pop();
}
if(root->right != nullptr){
	res,push_back(root->val);
	travesal(root->right,target - root->right->val,res);
	res.pop_back();
}

整体C++代码:

class Solution {
public:
vector<vector<int>> ans;
void travesal(TreeNode* root,int target,vector<int>& res){
    if(root->left == nullptr && root->right == nullptr && target == 0){
        ans.push_back(res);
        return;
    }
    if(root->left != nullptr){
        res.push_back(root->left->val);
        travesal(root->left,target - root->left->val,res);
        res.pop_back();
    }
    if(root->right != nullptr){
        res.push_back(root->right->val);
        travesal(root->right,target - root->right->val,res);
        res.pop_back();
    }
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
    if(nullptr == root) return ans;
    vector<int> res;
    res.push_back(root->val);
    travesal(root,targetSum-root->val,res);
    return ans;
}
};

从中序遍历序列构造二叉树 106

思路:
	后序遍历最后一个数一定是二叉树的根节点,再去中序遍历中找到该点,该点往左是以该节点为根节点的左子树,
	该点往右是以该节点为根节点的右子树,再以左子树序列去切割后序数组,一层层切割下去,每次后序数组最后一个就是节点元素

*从中序遍历与后序遍历序列构造二叉树 106

思路:

  • 1.后序数组大小为0说明是空节点
  • 2.如果不为空,去后序数组最后一个元素作为节点元素
  • 3.找到后序数组最后一个元素再中序数组的位置,作为切割点
  • 4.切割中序数组,切成中序左数组和右数组
  • 5.切割后序数组,切成后序左数组和后序右数组(中序数组大小一定是和后序数组的大小相同的)
  • 6.递归处理左区间与右区间

C++完整代码:

class Solution {
private:
TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
    if (postorder.size() == 0) return NULL;

    // 后序遍历数组最后一个元素,就是当前的中间节点
    int rootValue = postorder[postorder.size() - 1];
    TreeNode* root = new TreeNode(rootValue);

    // 叶子节点
    if (postorder.size() == 1) return root;

    // 找到中序遍历的切割点
    int delimiterIndex;
    for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
        if (inorder[delimiterIndex] == rootValue) break;
    }

    // 切割中序数组
    // 左闭右开区间:[0, delimiterIndex)
    vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
    // [delimiterIndex + 1, end)
    vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );

    // postorder 舍弃末尾元素
    postorder.resize(postorder.size() - 1);

    // 切割后序数组
    // 依然左闭右开,注意这里使用了左中序数组大小作为切割点
    // [0, leftInorder.size)
    vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
    // [leftInorder.size(), end)
    vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());

    root->left = traversal(leftInorder, leftPostorder);
    root->right = traversal(rightInorder, rightPostorder);

    return root;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
    if (inorder.size() == 0 || postorder.size() == 0) return NULL;
    return traversal(inorder, postorder);
}
};

用下标索引写出的代码版本:

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 NULL;

    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;

    // 切割后序数组
    // 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd)
    int leftPostorderBegin =  postorderBegin;
    int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin; // 终止位置是 需要加上 中序区间的大小size
    // 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd)
    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.size() == 0 || postorder.size() == 0) return NULL;
    // 左闭右开的原则
    return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
};

最大二叉树 654

思路:

构造树采用前序遍历,先构造中节点在构造左子树和右子树

1.判断数组大小是否为空,为空时返回空节点
2.获取区间最大值下标
3.构造二叉树节点
4.递归构造左子树及右子树

递归三部曲:

1.确定返回值及输入参数

参数就是存放元素的数组,返回构造二叉树的头节点

2.确定中止条件

  if(left ==  right) return nullptr;//传入的区间为空时直接返回

3.确定单层递归逻辑:

  int maxindex = left;
    for(int i = left+1;i < right;i++){
        if(nums[i] > nums[maxindex]){
            maxindex = i;
        }
    }
    TreeNode* root = new TreeNode(nums[maxindex]);
    root->left = travesal(nums,left,maxindex);//[0,index)
    root->right = travesal(nums,maxindex+1,right);//[index+1,right)

整体C++代码:

TreeNode* travesal(vector<int>& nums,int left, int right){
    if(left ==  right) return nullptr;
    int maxindex = left;
    for(int i = left+1;i < right;i++){
        if(nums[i] > nums[maxindex]){
            maxindex = i;
        }
    }
    TreeNode* root = new TreeNode(nums[maxindex]);
    root->left = travesal(nums,left,maxindex);//[0,index)
    root->right = travesal(nums,maxindex+1,right);//[index+1,right)
    return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
    return travesal(nums,0,nums.size());
}

合并二叉树 617

思路:

本题使用前序遍历,直接再root1上构造新节点,返回root1
考虑出现的所有情况:
1.root1为空,root2为空,无节点需要构造,返回nullptr
2.root1为空,root2不为空,构造值为root2->val的节点,返回该节点
3.root1不为空,root2不为空 无节点需要构造,节点相加
4.root1不为空,root2为空,无节点需要构造,返回nullptr

递归三部曲:

1.确定函数参数及返回值:

输入左右树根节点,返回构造完的新树
TreeNode* travesal(TreeNode* root1, TreeNode* root2);

2.确定中止条件:

if(root1 != nullptr && root2 == nullptr) return nullptr;
if(root1 == nullptr && root2 == nullptr) return nullptr;

3.确定单层递归逻辑:

 if(root1 != nullptr && root2 != nullptr) root1->val = root1->val + root2->val;
 if(root1 == nullptr && root2 != nullptr) return new TreeNode(root2->val);
 TreeNode* cur1 = nullptr;
    cur1 = mergeTrees(root1->left,root2->left);
    if(cur1 != nullptr){
        root1->left = cur1;
    }
    TreeNode* cur2 = nullptr;
    cur2 = mergeTrees(root1->right,root2->right);
    if(cur2 != nullptr){
        root1->right = cur2;
    }

整体C++代码:

TreeNode* travesal(TreeNode* root1, TreeNode* root2){
    if(root1 != nullptr && root2 != nullptr) root1->val = root1->val + root2->val;
    if(root1 == nullptr && root2 == nullptr) return nullptr;
    if(root1 == nullptr && root2 != nullptr) return new TreeNode(root2->val);
    if(root1 != nullptr && root2 == nullptr) return nullptr;
    TreeNode* cur1 = nullptr;
    cur1 = mergeTrees(root1->left,root2->left);
    if(cur1 != nullptr){
        root1->left = cur1;
    }
    TreeNode* cur2 = nullptr;
    cur2 = mergeTrees(root1->right,root2->right);
    if(cur2 != nullptr){
        root1->right = cur2;
    }
    return root1;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
   if(root1 == nullptr) return root2;
   if(root2 == nullptr) return root1;
   travesal(root1,root2);
   return root1;
}

*验证二叉搜索树 98

思路:

不能单纯的比较左节点小于中间节点,右节点大于中间节点
if (root->val > root->left->val && root->val < root->right->val) {
return true;
} else {
return false;
}	
我们要比较的是左子树左右节点小于中间节点,右子树所有节点大于中间节点,所以以上代码的逻辑是错误的 

例如:

注意题目中最小节点是int的最小值,所以要定义能表示更小数的类型,即long

我们采用中序遍历(二叉搜索树中序遍历是递增序列),递归的同时判断是否递增

递归三步:

1.确定递归函数返回值及输入参数:

判断树是否为二叉搜索树,输入树的根节点,以及上次遍历到节点值(初始化为INT_MIN-1)
bool travesal(TreeNode* root,long& pre_val)

2.确定中止条件:

if(root == nullptr) return true;

3.确定单层递归逻辑

 bool leftflag = travesal(root->left,pre_val);
    if(false == leftflag){
      result = false;
      return result;
    } 
    

    long val = root->val;
    if(pre_val < val){
        pre_val = val;
    }
    else return false;

   
    bool rightflag = travesal(root->right,pre_val);
    if(false == rightflag){
        result = false;
        return result;
    }

整体C++代码:

class Solution {
public:
bool result = true;
bool travesal(TreeNode* root,long& pre_val){
    if(root == nullptr) return true;

    bool leftflag = travesal(root->left,pre_val);
    if(false == leftflag){
      result = false;
      return result;
    } 
    

    long val = root->val;
    if(pre_val < val){
        pre_val = val;
    }
    else return false;

   
    bool rightflag = travesal(root->right,pre_val);
    if(false == rightflag){
        result = false;
        return result;
    }

    return result;
}
bool isValidBST(TreeNode* root) {
long pre_val = -2147483649;
 return  travesal(root,pre_val);
}
};

本文图片来源:
https://programmercarl.com/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E9%A2%98%E7%9B%AE%E5%88%86%E7%B1%BB%E5%A4%A7%E7%BA%B2%E5%A6%82%E4%B8%8B

posted @ 2022-09-26 15:40  WetYu  阅读(9)  评论(0)    收藏  举报