二叉树的定义
// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
注意点:
- 构造一个二叉树结点:
TreeNode * node = new TreeNode(21);
1)513. 找树左下角的值
左下角节点的定义:深度最大一层中最左边的节点。
思路:1. 用层次遍历的方法(迭代法)
2. 递归的方法
方法1:
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
TreeNode *result, *node;
std::queue<TreeNode*> que;
int que_size;
//根节点不为空 则进队
if(root) que.push(root);
while(!que.empty()){
//记录这一层的第一个节点
result = que.front();
que_size = que.size();
for(int i=0; i<que_size; i++){
//依次取这一层当中的节点
node = que.front();
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return result->val;
}
};
- 代码有冗余,改进:1)对于某一层中,第一个元素取了两次;可以用i=0记录第一个元素;
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
std::queue<TreeNode*> que;
int que_size, result; // 记录当前队长,每一层最左边的节点的值
//根节点不为空 则进队
if(root) que.push(root);
while(!que.empty()){
que_size = que.size();
for(int i=0; i<que_size; i++){
//依次取队列中节点
TreeNode* node = que.front();
if(i==0) result = node->val;
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return result;
}
};
复习要点:
- 容器适配器有stack、queue、priority_queue;
- 对于queue的方法:
push()在队尾增加一个元素;pop()从队列前面删除元素无返回值;front()返回队首 但不删除;
- 对于queue: back()返回最后一个元素的引用 不删除;
- 对于priority_queue:top()返回最后一个元素的引用 不删除;
疑问点:下面代码的顺序???
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
方法2:递归法
class Solution {
public:
//全局变量
int result=0, maxdepth=-1;
void traversal(TreeNode *node, int depth){
if(!node->left && !node->right){
if(depth > maxdepth){
maxdepth = depth;
result = node->val;
}
return;
}
if(node->left){
depth += 1;
traversal(node->left, depth);
depth -= 1;
}
if(node->right){
depth +=1;
traversal(node->right, depth);
depth -=1;
}
return;
}
int findBottomLeftValue(TreeNode* root) {
//深度从0开始
if(root) traversal(root, 0);
return result;
}
};
递归法总结:
遍历方式:因为找深度最大的最左边节点值,没有中的处理逻辑,故中左右、左中右、左右中的处理逻辑都行。
用全局遍历记录maxdepth;递归depth,以及回溯的过程。
3部曲(返回值 参数; 终止条件; 单层递归逻辑)
终止条件:这个地方一定是大于,如果是大于等于,最后留下的会是二叉树做大深度最右边的节点。
2)112. 路径总和
思想:回溯,中的处理逻辑没有。
若traversal某个节点为true,即遇到叶子节点返回true,则直接返回true,即将true放回上一层节点。
class Solution {
public:
bool traversal(TreeNode *node, int curval){
if(!node->left && !node->right && curval==0){
return true;
}
if(!node->left && !node->right && curval!=0){
return false;
}
//左
if(node->left){
curval -= node->left->val;
if (traversal(node->left, curval) == true) return true;
curval += node->left->val;
}
//右
if(node->right){
curval -= node->right->val;
if (traversal(node->right, curval) == true) return true;
curval += node->right->val;
}
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root) return false;
//先减去当前节点的值作为curval
return traversal(root, targetSum-(root->val));
}
};
3) 113. 路径总和 II
- 对path做push_back()的操作所在位置,首先主函数中的头节点别落下。
- 在 traverse()函数中需要return;语句吗?
class Solution {
public:
void traverse(TreeNode *node, vector<vector<int>> &result, vector<int> &path, int curval){
if(!node->left && !node->right && curval==0){
//收获结果
result.push_back(path);
return;
}
// if(!node->left && !node->right && curval!=0) return;
if(node->left){
curval -= node->left->val;
path.push_back(node->left->val);
traverse(node->left, result, path, curval);
curval += node->left->val;
path.pop_back();
}
if(node->right){
curval -= node->right->val;
path.push_back(node->right->val);
traverse(node->right, result, path, curval);
curval += node->right->val;
path.pop_back();
}
return;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<vector<int>> result;
if(!root) return result;
vector<int> path;
//头节点加入path
path.push_back(root->val);
traverse(root, result, path, targetSum-root->val);
return result;
}
};
4)106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树
中序+后序构造二叉树:
- 构建过程中,对于中序、后序序列分割的思路
- 递归逻辑
说到一层一层切割,就应该想到了递归。
- 第一步:如果数组大小为零的话,说明是空节点了。
- 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
- 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
- 第四步:切割中序数组,切成中序左数组和中序右数组(顺序别搞反了,一定是先切中序数组)
- 第五步:切割后序数组,切成后序左数组和后序右数组
- 第六步:递归处理左区间和右区间
class Solution {
public:
TreeNode* traverse(vector<int>& inorder, vector<int>& postorder){
if(postorder.size()==0 || inorder.size()==0) return NULL;
//创建根结点
int resultval = postorder[postorder.size()-1];
TreeNode *root = new TreeNode(resultval);
if(postorder.size()==1) return root;
//分割根结点在中序序列中的索引
int segmentidx;
for(segmentidx=0; segmentidx<inorder.size(); segmentidx++){
if(inorder[segmentidx]==resultval)
break;
}
//左闭右开
//分割中序序列
vector<int> leftinorder(inorder.begin(), inorder.begin()+segmentidx);
vector<int> rightinorder(inorder.begin()+segmentidx+1, inorder.end());
//分割后续序列
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 = traverse(leftinorder, leftpostorder);
root->right = traverse(rightinorder, rightpostorder);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.size()==0 || postorder.size()==0){
return NULL;
}
return traverse(inorder, postorder);
}
};