题目链接:513. 找树左下角的值
思路
就是求最下面一层的最左边的那个结点的值,一般我们按顺序左右遍历,第一次遍历到的最深处的那条路径的左叶子就是我们要求的值了,比较深度一般就是用前序了
其实就是求所有路径中最长的一条路径的左叶子。
递归法(前序)
class Solution {
public:
int maxdept = INT_MIN;//计入每一次遍历到叶子的高度,如果这一次不是最高就换
int result;//结果值
void traversal(TreeNode* root, int dept) {
if (root == nullptr) return;//如果遍历到空节点就返回
if (root->left == nullptr && root->right == nullptr) {//如果遍历到叶子结点就看与上次相比是不是更深,更深就换值
if (dept > maxdept) {
maxdept = dept;
result = root->val;
}
return;
}
dept++;//遍历下一层深度+1
traversal(root->left, dept);
dept--;//这两行可以删掉
dept++;
traversal(root->right, dept);
dept--;(回溯)
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
递归三部曲
1.确定返回类型和参数类型:因为是内部变化,且一般记录结果用void类型,这里用void,参数为根结点和当前的深度,记录值的一般不许要返回类型
2.确定终止条件:当到达根结点就记录比较,如果没到根节点到中间结点遍历左边的时候为空就返回或遍历到右结点右结点为空就返回上一层遍历左节点
3.确定单层递归的逻辑:前序遍历,就每次遍历都记录深度(这里有两个遍历,分别为左子树和右子树),会到上一层深度也要-1,相当于回溯。
二叉树原则回顾
递归函数前一行是遍历下一层的逻辑,如果只是上一行就只会影响一个递归,如果在终止条件之前会影响全部递归
递归函数下面一般写回溯逻辑。逻辑看根,理解(模拟+检查)看后几层
有重复代码块就能使用递归
后面题再补充
题目链接:112. 路径总和
递归法(前序)
class Solution {
public:
int res;
void traversal(TreeNode* root, int targetSum, int sum) {
if (root == nullptr) return;
if (root->left == nullptr && root->right == nullptr) {
sum += root->val;
if (sum == targetSum){
res++;
}
sum -= root->val;
return;
}
sum += root->val;
traversal(root->left, targetSum, sum);
traversal(root->right, targetSum, sum);
sum -= root->val;
}
bool hasPathSum(TreeNode* root, int targetSum) {
traversal(root, targetSum , 0);
return res > 0? true:false;
}
};
题目链接:113. 路径总和 II
class Solution {
public:
int sum(vector<int>& a) {
int psum = 0;
for (int i = 0; i < a.size(); i++) {
psum += a[i];
}
return psum;
}
void traversal(TreeNode* root, int targetSum, vector<vector<int>>& result, vector<int>& path) {
if (root == nullptr) return;
if (root->left == nullptr && root-> right == nullptr) {
path.push_back(root->val);
if (sum(path) == targetSum) result.push_back(path);
path.pop_back();
return;
}
path.push_back(root->val);
traversal(root->left, targetSum, result, path);
traversal(root->right, targetSum, result, path);
path.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<vector<int>>result;
vector<int>path;
traversal(root,targetSum,result,path);
return result;
}
};
和第一题差不多,但也有很多需要注意的点:
当回溯的时候这个点才遍历完左结点还需要用到就先不能把他删除,就只需要在最后删除就行了,写一个pop,注意加结点的顺序。
注意逻辑
题目链接:106. 从中序与后序遍历序列构造二叉树
class Solution {
public:
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);
//分割数组,为接下来的递归提供准备,准备两颗子树的中后序数组
postorder.resize(postorder.size() - 1);
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
vector<int>leftinorder(inorder.begin(), inorder.begin() + delimiterIndex);
vector<int>rightnorder(inorder.begin() + delimiterIndex + 1, inorder.end());
vector<int>leftpostorder(postorder.begin(), postorder.begin() + leftinorder.size());
vector<int>rightpostorder(postorder.begin() + leftinorder.size(), postorder.end());
//确定单层逻辑,单层逻辑就是重复逻辑,因为你每一步都是一样的,确定每一个子树前后序数组就行了
//因为是自上而下得出结点所以后序
root->left = traversal(leftinorder, leftpostorder);
root->right = traversal(rightnorder, rightpostorder);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
return traversal(inorder, postorder);
}
};
题目链接:105. 从前序与中序遍历序列构造二叉树
class Solution {
public:
TreeNode*traversal(vector<int>& preorder, vector<int>& inorder, int size) {
//确定终止条件:当到叶子结点下一层后,返回空,到递归函数的下一层
if (preorder.size() == 0) return NULL;
//通过后序数组确定根
int rootValue = preor.
der[size - preorder.size()];
TreeNode* root = new TreeNode(rootValue);
//分割数组,为接下来的递归提供准备
preorder.pop_back();
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
vector<int>leftinorder(inorder.begin(), inorder.begin() + delimiterIndex);
vector<int>rightinorder(inorder.begin() + delimiterIndex + 1, inorder.end());
vector<int>leftpreorder(preorder.begin(), preorder.begin() + leftinorder.size());
vector<int>rightpreorder(preorder.begin() + leftinorder.size(), preorder.end());
//确定单层逻辑,单层逻辑就是重复逻辑,因为你每一步都是一样的,确定每一个子树前后序数组就行了
root->left = traversal(leftpreorder, leftinorder, size);
root->right = traversal(rightpreorder, rightinorder, size);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int size = preorder.size();
return traversal(preorder, inorder, size);
}
};
递归三部曲
确定返回类型和参数:返回类型为结点,表示根结点,
确定终止条件:当遇到叶子结点的下一节点就是遍历数组为空时,返回NULL
确定单层循环逻辑,因为每次都要减一个元素,所以先求出根结点再求出递归需要的准备,因为都是由根结点步步进行直到根结点所以用后序,先遍历左子树,再遍历右子树,再返回根
重点
当有多个重复性代码可以用递归解决:这里每次都要遍历左右确定根,只需要最后写终止条件就可以了。
每次return都会到各种递归函数的下一行,值返回给递归函数