文章目录
129. 求根节点到叶节点数字之和


深度优先遍历DFS
步骤
- 确定递归函数返回值及其参数
- 返回值为void
- 参数:根节点root,记录最终结果的整型的全局变量result,记录路径的数组的全局变量path。
- 确定终止条件
遇到叶子节点,收集结果并返回本层递归。注意,单条路径的结果path用vector是为了方便回溯,所以需要一个函数vectorToInt把vector转成int。 - 确定递归单层逻辑
前中后序都可以,主要是当左节点不为空,path收集路径,并递归左孩子,右节点同理,注意要回溯。
注意回溯和递归要永远一起,一个递归,对应一个回溯,是一对一的关系
/**
* 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) {}
* };
*/
class Solution {
public:
int sumNumbers(TreeNode* root) {
path.clear();
if(root == nullptr) return 0;//剔除
//中节点
path.push_back(root->val);
travelsal(root);//回溯
return result;
}
private:
int result = 0;//记录最终结果
vector<int> path;//记录单路径结果
//单路径结果保存在数组 需要转成int
int vector2Int(const vector<int>& vec)
{
int sum = 0;
for(int i=0; i<vec.size(); i++)
{
//数字构成是节点拼接 1->2 --- 12 所以要乘10
sum = sum * 10 + vec[i];
}
return sum;
}
//回溯
void travelsal(TreeNode* cur)
{
//遇到叶子节点 保存结果并返回
if(!cur->left && !cur->right)
{
//单路径节点值拼接
result += vector2Int(path);
return;
}
//前序遍历 中左右
if(cur->left)//左节点
{
path.push_back(cur->left->val);//保存单路径结果
travelsal(cur->left);//递归
path.pop_back();//回溯
}
if(cur->right)
{
path.push_back(cur->right->val);
travelsal(cur->right);
path.pop_back();
}
return;
}
};
广度优先遍历BFS
/**
* 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) {}
* };
*/
class Solution {
public:
int sumNumbers(TreeNode* root) {
//2.广度优先遍历
return BFS(root, 0);
}
private:
//2.广度优先遍历
int BFS(TreeNode* cur, int sum)
{
if(!cur) return 0;//空树
//当前层遍历 遇到叶子节点 结束递归
else if(!cur->left && !cur->right) return sum * 10 + cur->val;
return BFS(cur->left, sum * 10 + cur->val) + BFS(cur->right, sum * 10 + cur->val);
}
};
112. 路径总和


深度优先遍历DFS 递归
步骤
- 确定递归函数返回值及其参数
- 返回值:不需要遍历整棵树,需要返回值,bool类型
- 参数:二叉树根节点,int类型计数器count,计算二叉树的一条边之和是否正好是目标和
- 确定终止条件
计数器count初始为目标和,然后每次减去遍历路径节点上的数值:
- count = 0,同时到达叶子节点,找到了目标和,true
- 遍历到叶子节点,count不为0,没找到,false
- 确定递归单层逻辑
- 空节点不进入递归,终止条件是判断叶子节点
- 递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,结束递归
注意回溯和递归要永远一起,一个递归,对应一个回溯,是一对一的关系
写法1
/**
* 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) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
//1.深度优先遍历 递归 写法1
if(root == nullptr) return false;
return travelsal(root, targetSum - root->val);//中节点
}
private:
//1.深度优先遍历 递归
bool travelsal(TreeNode* cur, int count)
{
//遍历到叶子节点且count为0 找到路径
if(!cur->left && !cur->right && count == 0) return true;
//遍历到叶子节点且count不为0 没有找到路径
if(!cur->left && !cur->right && count != 0) return false;
//左节点
if(cur->left)
{
count -= cur->left->val;
if(travelsal(cur->left, count)) return true;//递归
count += cur->left->val;//回溯
}
//右节点
if(cur->right)
{
count -= cur->right->val;
if(travelsal(cur->right, count)) return true;
count += cur->right->val;
}
return false;
}
};
写法2,简化代码
/**
* 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) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
//1.深度优先遍历 递归 写法2
if(!root) return false;//空树
if(!root->left && !root->right && targetSum == root->val) return true;//遍历到叶子节点且找到路径
//没有遍历到叶子节点 继续递归
//遍历左右节点 targetSum减去上一个节点值
return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
}
};
广度优先遍历BFS 迭代
用栈模拟递归,栈里一个元素要记录该节点指针和从头结点到该节点的路径数值总和。
用pair结构来存放这个栈里的元素,定义为:pair<TreeNode*, int> pair<节点指针,路径数值>
/**
* 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) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
//2.迭代 栈模拟 pair对组
if(root == nullptr) return false;
//栈里要放的是pair<节点指针,路径数值>
stack<pair<TreeNode*, int>> st;
st.push(pair<TreeNode*, int>(root, root->val));//中节点
while(!st.empty())
{
//获取栈顶元素
pair<TreeNode*, int> node = st.top();
st.pop();//弹出
//到叶子节点且路径和为目标值 找到路径
if(!node.first->left && !node.first->right && node.second == targetSum) return true;
if(node.first->left)//左节点
{
//栈保存(左节点,左节点值+累加和)
st.push(pair<TreeNode*, int>(node.first->left, node.first->left->val + node.second));
}
if(node.first->right)
{
st.push(pair<TreeNode*, int>(node.first->right, node.first->right->val + node.second));
}
}
return false;
}
};
113. 路径总和 II


深度优先遍历BFS 步骤
- 确定递归函数返回值及其参数
- 返回值:找出所有路径,遍历整棵树,不需要返回值
- 参数:二叉树根节点,int类型计数器count,计算二叉树的一条边之和是否正好是目标和,保存一条路径的数组path,保存所有路径的数组result
- 确定终止条件
计数器count初始为目标和,然后每次减去遍历路径节点上的数值。如果count = 0,同时到达叶子节点,找到了目标和,保存单条路径结果 - 确定递归单层逻辑
- 空节点不进入递归,终止条件是判断叶子节点
- 如果找到一条符合要求的路径,就在result中保存这条路径path
注意回溯和递归要永远一起,一个递归,对应一个回溯,是一对一的关系
写法1
这种写法,有两个回溯,一个是计数器count,还有一个是path,路径回溯
/**
* 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) {}
* };
*/
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
path.clear();
result.clear();
if(root == nullptr) return result;
//1.深度优先遍历BFS 写法1
path.push_back(root->val);//中节点
travelsal(root, targetSum - root->val);
return result;
}
private:
vector<int> path;//单条路径
vector<vector<int>> result;//所有结果
void travelsal(TreeNode* cur, int count)
{
if(!cur->left && !cur->right && count == 0)
{
result.push_back(path);
return;
}
if(cur->left)
{
path.push_back(cur->left->val);//保存当前节点值
count -= cur->left->val;//计数
travelsal(cur->left, count);//递归
count += cur->left->val;//回溯
path.pop_back();//回溯
}
if(cur->right)
{
path.push_back(cur->right->val);
count -= cur->right->val;
travelsal(cur->right, count);
count += cur->right->val;
path.pop_back();
}
return;
}
};
写法2
/**
* 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) {}
* };
*/
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
path.clear();
result.clear();
//1.深度优先遍历BFS 写法2
dfs(root, targetSum);
return result;
}
private:
vector<int> path;//单条路径
vector<vector<int>> result;//所有结果
//1.深度优先遍历BFS 写法2
void dfs(TreeNode* cur, int count)
{
if(!cur) return;
path.push_back(cur->val);
count -= cur->val;
if(!cur->left && !cur->right && count==0) result.push_back(path);
dfs(cur->left, count);//递归
dfs(cur->right, count);//递归
path.pop_back();//回溯
}
};
1382.将二叉搜索树变平衡


有序树转成有序数组,再转成平衡二叉树
/**
* 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) {}
* };
*/
class Solution {
public:
TreeNode* balanceBST(TreeNode* root) {
dfs(root);
return getBalanceTree(vec, 0, vec.size()-1);
}
private:
//1.有序树转成有序数组 中序遍历 节点值大小排序是左中右
vector<int> vec;
void dfs(TreeNode* cur)
{
if(cur == nullptr) return;
dfs(cur->left);//左节点
vec.push_back(cur->val);
dfs(cur->right);
}
//2.有序数组转成平衡树
TreeNode* getBalanceTree(vector<int>& vec, int start, int end)
{
if(start > end) return nullptr;
int mid = start + (end - start) / 2;//头节点值
TreeNode* root = new TreeNode(vec[mid]);//头节点
root->left = getBalanceTree(vec, start, mid-1);
root->right = getBalanceTree(vec, mid+1, end);
return root;
}
};
100. 相同的树


四种情况:
- 都是空树,true
- 其中一个是空树,false
- 树不空且有一个节点值不相等,false
- 树不空且左右子树都相同,true
DFS 递归法
/**
* 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) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p == nullptr && q == nullptr) return true;
else if(p == nullptr || q == nullptr) return false;
else if(p->val != q->val) return false;
//树不空 比较两个子树
bool left = isSameTree(p->left, q->left);//递归
bool right = isSameTree(p->right, q->right);//递归
return left && right;//左右子树都相同才是相同的树
}
};
BFS 迭代法
/**
* 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) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
//2.BFS 迭代法 队列
if(p == nullptr && q == nullptr) return true;
else if(p == nullptr || q == nullptr) return false;
queue<TreeNode*> que;
que.push(p);
que.push(q);
while(!que.empty())
{
//分别获取两个数的节点
TreeNode* nodep = que.front(); que.pop();
TreeNode* nodeq = que.front(); que.pop();
//都是空节点跳过当前两个节点 下一轮迭代
if(!nodep && !nodeq) continue;
//有一个节点是空指针 或者两个节点值不相等 false
if(!nodep || !nodeq || (nodep->val != nodeq->val)) return false;
//更新队列节点
que.push(nodep->left); que.push(nodeq->left);
que.push(nodep->right); que.push(nodeq->right);
}
return true;
}
};
116. 填充每个节点的下一个右侧节点指针


BFS 迭代
- 用队列,时间复杂度 O ( N ) O(N) O(N),空间复杂度 O ( N ) O(N) O(N)
- 遍历每一行的时候,如果不是最后一个Node,则指向下一个Node;如果是最后一个Node,则指向nullptr
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
//1.BFS 队列
queue<Node*> que;
if(root != NULL) que.push(root);
while(!que.empty())
{
//依次遍历每行的节点
int size = que.size();
for(int i=0; i<size; i++)
{
//获取当前节点
Node* cur = que.front();
que.pop();
//如果不是最后一个节点 指向下一个节点,否则指向nullptr
if(i != size-1) cur->next = que.front();
else cur->next = nullptr;
//更新队列节点
if(cur->left != nullptr) que.push(cur->left);
if(cur->right != nullptr) que.push(cur->right);
}
}
return root;
}
};
DFS 递归
题目进阶要求就是要使用递归,时间复杂度
O
(
N
)
O(N)
O(N),空间复杂度
O
(
1
)
O(1)
O(1)
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
//2.DFS
dfs(root);
return root;
}
private:
void dfs(Node* cur)
{
if(cur == nullptr) return;
//中节点 处理
if(cur->left) cur->left->next = cur->right;//左孩子
if(cur->right)//右孩子
{
if(cur->next) cur->right->next = cur->next->left;
else cur->right->next = nullptr;
}
dfs(cur->left);
dfs(cur->right);
}
};
相关题目的二叉树总结点
1. 二叉树遍历方式:
- 深度优先遍历DFS
- 前序遍历(递归法,迭代法)
- 中序遍历(递归法,迭代法)
- 后序遍历(递归法,迭代法)
- 广度优先遍历BFS:层次遍历(迭代法)
2. 二叉树返回值:
递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值——113.路径总和Ⅱ
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值——236. 二叉树的最近公共祖先
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回——112. 路径总和
3. 二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
4. 二叉搜索树的应用: 与有序数组的转化、验证二叉搜索树、与平衡树的转化
5. 二叉平衡树的特征: 一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1
6. 完美二叉树的特征 ——题116