513.找树左下角的值
题目链接/文章讲解/视频讲解: 代码随想录
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
int result = 0;
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (i == 0) result = node->val; // 记录最后一行第一个元素
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
一开始想到的就是层序遍历,使用迭代法进行遍历。只需要在层序遍历的过程中,只记录每层第一个元素,这样就能每层记录下第一个元素,在下一层将上一层的覆盖住,到最后一层就是树左下角的值了!
采用递归方法的话,代码如下:
class Solution {
public:
int maxDepth = INT_MIN;
int result;
void traversal(TreeNode* root, int depth) {
if (root->left == NULL && root->right == NULL) {
if (depth > maxDepth) {
maxDepth = depth;
result = root->val;
}
return;
}
if (root->left) {
depth++;
traversal(root->left, depth);
depth--; // 回溯
}
if (root->right) {
depth++;
traversal(root->right, depth);
depth--; // 回溯
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
在递归函数中,传入一个父节点、一个深度两个参数。由于在递归函数中,只需要对在终止条件时(即遍历到叶子结点时)进行判断修改结果值,所以没有对中间节点的判断,把这个遍历当做前序遍历还是后序遍历都可以。在终止条件时,对叶子结点的深度进行判断,如果大于现在的最大深度,则要更新最大深度和结果值以便下一次的判断。在左右遍历时,首先要现将深度++,在遍历回来后要将深度--这样就体现了回溯思想!在遍历回本节点时子节点加的深度应该要减去,再从本节点重新遍历。也可以化简成这样:
if (root->left) {
traversal(root->left, depth + 1); // 隐藏着回溯
}
这样就会在递归回来后depth的值和递归前一样,也就实现了回溯。
112. 路径总和 113.路径总和ii
题目链接/文章讲解/视频讲解: 代码随想录
class Solution {
private:
bool traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == cur->val) return true;
if (cur->left) {
if (traversal(cur->left, count-cur->val)) return true;
}
if (cur->right) {
if(traversal(cur->right, count-cur->val)) return true;
}
return false;
}
public:
bool hasPathSum(TreeNode* root, int sum) {
if (root == NULL) return false;
return traversal(root, sum);
}
};
这是我的做法,使用递归做法,首先判断终止条件是如果该节点是叶子节点就判断与所要求的路径和是否相同,如果相同则返回true。然后遍历左右节点,传入的路径和是减掉当前节点的值的,如果左节点或右节点到叶子结点的路径符合要求,就返回true。最后左右子树的所有路径都不满足的话就返回false。示例的思想是将该节点子节点的值减去,判断叶子结点的count值是否为零来确定结果的,思路是差不多的,只不过减少该节点的值的时间不同。
精简后的代码如下,只能说很难看懂了:
bool hasPathSum(TreeNode* root, int sum) {
if (!root) return false;
if (!root->left && !root->right && sum == root->val) {
return true;
}
return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
}
下面代码是路径总和ii的题目代码,思路和i的差不多,只不过在加上一个vector path用来记录全部路径。每次递归结束时要将path中的值弹出,这样就可以实现回溯的效果。
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& path, vector<vector<int>>& result, int sum) {
path.push_back(cur->val);
if (!cur->left && !cur->right && cur->val == sum) {
vector<int> temp(path);
result.push_back(temp);
return;
}
if (cur->left) {
traversal(cur->left, path, result, sum-cur->val);
path.pop_back();
}
if (cur->right) {
traversal(cur->right, path, result, sum-cur->val);
path.pop_back();
}
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<vector<int>> result;
if (root == NULL) return result;
vector<int> path;
traversal(root, path, result, targetSum);
return result;
}
};
下面代码是路径总和ii的题目代码,思路和i的差不多,只不过在加上一个vector path用来记录全部路径。每次递归结束时要将path中的值弹出,这样就可以实现回溯的效果。
105.从前序与中序遍历序列构造二叉树
题目链接/文章讲解/视频讲解: 代码随想录
TreeNode* buildTree(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 index; //获取根节点,对前序数组分割
for (index = 0; index < inorder.size(); index++) {
if (inorder[index] == rootValue)
break;
}
vector<int> leftinorder(inorder.begin(), inorder.begin()+index);
vector<int> rightinroder(inorder.begin()+index+1, inorder.end());
// postorder 舍弃末尾元素,因为这个元素就是中间节点,已经用过了
postorder.resize(postorder.size() - 1);
vector<int> leftpostorder(postorder.begin(), postorder.begin()+leftinorder.size());
vector<int> rightpostorder(postorder.begin()+leftinorder.size(), postorder.end());
root->left = buildTree(leftinorder, leftpostorder);
root->right = buildTree(rightinroder, rightpostorder);
return root;
}
使用前序和中序遍历序列构造二叉树代码如上,具体步骤有这几条:
-
第一步:如果数组大小为零的话,说明是空节点了。
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
-
第五步:切割后序数组,切成后序左数组和后序右数组
-
第六步:递归处理左区间和右区间
思路就是使用后序数组最后一个元素来确定根节点,然后将中序数组分割成左右子树两个部分;接下来再用分割成的两个部分的大小来对后序数组进行分割,将后序数组也分割成左右子树两个部分,然后分别递归根节点的左右子树,使用分割后的子数组来进行递归,最后返回根节点即可。
使用前序与中序遍历序列构造二叉树思路也一样,只不过是使用前序数组的第一个来分割中序数组!
注意:前序和后序不能唯一确定一棵二叉树!,因为没有中序遍历无法确定左右部分,也就是无法分割。