1. 513找树左下角的值
题目:给定一个二叉树,在数的最后一行找到最左边的值
示例 1:
示例 2:
1.1 递归法:
解题步骤:
在树的最后一行找到最左边的值。其实就是深度最大的叶子节点一定是最后一行
如何找最左边的呢?前中后序都可以(因为本题没有中间节点的处理逻辑),保证优先左边搜素,然后记录深度最大的叶子节点
1.确定递归函数的参数和返回值
参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。这里不需要返回值,所以递归函数的返回类型为void
本题还需要类里的两个全局变量,maxDepth
用来记录最大深度,result记录最大深度最左节点的数值。
代码如下:
int maxDepth = INT_MIN; // 全局变量,记录最大深度
int result; // 全局变量 最大深度最左节点的数值
void traversal(TreeNode* root, int depth)
2.确定终止条件
当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度。
3.确定单层递归的逻辑
在找最大深度的时候,递归的过程中依然要使用回溯,代码如下:
if (root->left) { // 左
depth++; // 深度加一
traversal(root->left, depth);
depth--; // 回溯,深度减一
}
if (root->right) { // 右
depth++; // 深度加一
traversal(root->right, depth);
depth--; // 回溯,深度减一
}
return;
完整代码如下:
#include<iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
// 1.确定递归函数的参数和返回值
int maxDepth = INT_MIN;
int result;
void traversal(TreeNode* node, int depth) {
// 2.确定终止条件
if (node->left == nullptr && node->right == nullptr) { //遇到叶子节点
if (depth > maxDepth) {
maxDepth = depth;
result = node->val;
}
return;
}
// 3.确定单层递归的逻辑
if (node->left) {
depth++;
traversal(node->left, depth);
depth--; // 回溯
}
if (node->right) {
depth++;
traversal(node->right, depth);
depth--; // 回溯
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
int main(void) {
Solution solu;
TreeNode* root = new TreeNode(2);
root->left = new TreeNode(1);
root->right = new TreeNode(3);
cout << solu.findBottomLeftValue(root) << endl;
system("pause");
return 0;
}
1.2 迭代法:
代码如下:
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que; //定义队列
if (root != nullptr) 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;
}
};
2. 112路径总和
题目:给定一个二叉树和一个目标和,判断该树种是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和
示例: 给定如下二叉树,以及目标和 sum = 22,
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
解题步骤:
1.确定递归函数的参数和返回值
参数:需要二叉树的根节点,还需要一个计数器,用来计算二叉树的一条路径之和是否正好是目标和,计数器为int型
递归函数是否需要返回值,总结三点如下:
-
如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
-
如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先 (opens new window)中介绍)
-
如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
图中可以看出,遍历的路线,并不要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示。
2.确定终止条件
首先计数器如何统计这一条路径的和呢?
不要去累加然后判断是否等于目标和,那么代码比较麻烦,可以用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。
如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。
如果遍历到了叶子节点,count不为0,就是没找到。
用目标值与节点值相减判断是否符合要求比节点值相加再判断要方便
3.确定单层递归的逻辑
因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。
递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回。
代码如下:
#include<iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
// 1.确定递归函数的参数和返回值
bool traversal(TreeNode* node, int count) {
// 2.确定终止条件
if (!node->left && !node->right && count == 0) return true; // 遇到叶子节点,并且计数器为0
if (!node->left && !node->right) return false; // 遇到叶子节点,计数器不为0的返回flase
if (node->left) {
count -= node->left->val;
if (traversal(node->left, count)) return true; // 如果左子树找到了,不用再寻找了,直接返回true
count += node->left->val;
}
if (node->right) {
count -= node->right->val;
if (traversal(node->right, count)) return true; //右子树找到则返回true
count += node->right->val;
}
return false; // 左右子树都没找到,返回false
}
};
int main(void) {
Solution solu;
TreeNode* root = new TreeNode(5);
root->left = new TreeNode(4);
root->right = new TreeNode(8);
root->left->left = new TreeNode(11); // 5 4 8 11 null 13 4 7 2 null null null 1
root->left->left->left = new TreeNode(7);
root->left->left->right = new TreeNode(2);
root->right->left = new TreeNode(13);
root->right->right = new TreeNode(4);
root->right->right->right = new TreeNode(1);
cout << solu.traversal(root, 22) << endl;
system("pause");
return 0;
}
3. 113路径总和II
题目:给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径
示例: 给定如下二叉树,以及目标和 sum = 22,
113.路径总和ii要遍历整个树,找到所有路径,所以递归函数不要返回值!
如图:
代码如下:
#include<iostream>
#include<vector>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
//1. 确定递归函数的参数和返回值
void traversal(TreeNode* cur, int count) {
// 2.确定终止条件
if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径
result.push_back(path);
return;
}
if (!cur->left && !cur->right) return; // 遇到叶子节点而没有找到合适的边,直接返回
if (cur->left) {
path.push_back(cur->left->val);
count -= cur->left->val;
traversal(cur->left, count); // 递归
count += cur->left->val; // 回溯
path.pop_back(); // 回溯
}
if (cur->right) {
path.push_back(cur->right->val);
count -= cur->right->val;
traversal(cur->right, count);
count += cur->right->val;
path.pop_back();
}
return;
}
vector<vector<int>> pathSum(TreeNode* root, int sum) {
result.clear();
path.clear();
if (root == nullptr) return result;
path.push_back(root->val);
traversal(root, sum - root->val);
return result;
}
};
int main(void) {
Solution solu;
TreeNode* root = new TreeNode(5);
root->left = new TreeNode(4);
root->left->left = new TreeNode(11);
root->left->left->left = new TreeNode(7);
root->left->left->right = new TreeNode(2);
root->right = new TreeNode(8);
root->right->left = new TreeNode(13);
root->right->right = new TreeNode(4);
root->right->right->left = new TreeNode(5);
root->right->right->right = new TreeNode(1);
vector<vector<int>> result = solu.pathSum(root, 22);
for (int i = 0; i < result.size(); i++) {
for (int j = 0; j < result[0].size(); j++) {
cout << result[i][j] << " ";
}
cout << endl;
}
system("pause");
return 0;
}
4. 106从中序与后序遍历序列构造二叉树
题目:根据一棵树的中序遍历与后序遍历构造二叉树,可以假设树中没有重复元素
示例:
-
中序遍历 inorder = [9,3,15,20,7]
-
后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树:
思路:
以后续数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组,一层一层切下去,每次后序数组最后一个元素就是节点元素。
-
第一步:如果数组大小为0的话,说明为空节点
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组(一定是先切中序数组)
-
第五步:切割后序数组,切成后序左数组和后序右数组
-
第六步:递归处理左区间和右区间
代码如下:
#include<iostream>
#include<vector>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {
// 1.后序数组为0,为空节点
if (postorder.size() == 0) return nullptr;
// 2.后序遍历数组最后一个元素,就是当前的中间节点
int rootValue = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootValue);
// 如果只有一个节点
if (postorder.size() == 1) return root;
// 3.寻找中序数组位置作为切割点
int index;
for (index = 0; index < inorder.size(); index++) {
if (inorder[index] == rootValue) break;
}
// 4.切割中序数组,得到中序左数组和中序右数组
vector<int> leftInorder(inorder.begin(), inorder.begin() + index); //左区间[0,index)
vector<int> rightInorder(inorder.begin() + index + 1, inorder.end()); //右区间[index+1,end)
// inorder.end()是指向末尾的指针,inorder.end()-1 才指向最后一个元素,所以左区间和右区间才是左闭右开
// 5.切割后序数组,得到后序左数组和后序右数组
postorder.resize(postorder.size() - 1); // 舍弃末尾元素
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
// 6.递归处理左区间和有区间
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);
return root;
}
TreeNode* bulidTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return nullptr;
return traversal(inorder, postorder);
}
};
int main(void) {
Solution solu;
vector<int> inorder = { 9,3,15,20,7 };
vector<int> postorder = { 9,15,7,20,3 };
TreeNode* result = solu.bulidTree(inorder, postorder);
cout << result->val << endl;
cout << result->left->val << " " << result->right->val << endl;
cout << result->right->left->val << " " << result->right->right->val << endl;
system("pause");
return 0;
}
5. 105从前序与中序遍历序列构造二叉树
题目:根据一棵树的前序遍历与中序遍历构造二叉树。
注意: 你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树:
代码如下:
#include<iostream>
#include<vector>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
TreeNode* traversal(vector<int>& inorder, vector<int>& preorder) {
// 1.空节点
if (preorder.size() == 0) return nullptr;
// 2.取前序第一个元素,为根节点
int rootValue = preorder[0];
TreeNode* root = new TreeNode(rootValue);
if (preorder.size() == 1) return root;
// 3.寻找中序数组位置作为切割点
int index;
for (index = 0; index < inorder.size(); index++) {
if (inorder[index] == rootValue) break;
}
// 4.切割中序数组
vector<int> leftInorder(inorder.begin(), inorder.begin() + index);
vector<int> rightInorder(inorder.begin() + index + 1, inorder.end());
// 5.切割前序数组
vector<int> leftPreorder(preorder.begin() + 1, preorder.begin() + 1 + leftInorder.size());
vector<int> rightPreorder(preorder.begin() + 1 + leftInorder.size(), preorder.end());
// 6.递归遍历
root->left = traversal(leftInorder, leftPreorder);
root->right = traversal(rightInorder, rightPreorder);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& preorder) {
if (inorder.size() == 0 || preorder.size() == 0) return nullptr;
return traversal(inorder, preorder);
}
};
int main(void) {
Solution solu;
vector<int> inorder = { 9,3,15,20,7 };
vector<int> preorder = { 3,9,20,15,7 };
TreeNode* result = solu.buildTree(inorder, preorder);
cout << result->val << endl;
cout << result->left->val << " " << result->right->val << endl;
cout << result->right->left->val << " " << result->right->right->val << endl;
system("pause");
return 0;
}