文章目录
- 引言
- 一、计算布尔二叉树的值
- 二、求根节点到叶节点数字之和
- 三、二叉树剪枝
- 四、验证搜索二叉树
- 五、二叉搜索树中第k小的元素
- 六、二叉树的所有路径
- 总结
引言
dfs在二叉树中有了更进一步地体现,通过二叉树中的dfs相关题型,深刻理解全局变量、回溯和剪枝。
一、计算布尔二叉树的值
思路:
- 先计算出左右子树的值,再根据当前结点的值进行逻辑运算
- 终止条件:叶子节点时,返回节点值
class Solution
{
public:
bool dfs(TreeNode* root)
{
if(!root->left && !root->right) return root->val;
bool left = dfs(root->left);
bool right = dfs(root->right);
return root->val == 2 ? left || right : left && right;
}
bool evaluateTree(TreeNode* root)
{
return dfs(root);
}
};
二、求根节点到叶节点数字之和
思路:
- 函数头设计:presum保存从根节点到当前节点路径的和,返回值代表左右子树所有路径之和
- presum设置为临时变量,以便回溯
- 每次先计算当前路径和sum,再返回左右子树所有路径之和
- 终止条件:root为空,则返回0;root为叶子节点,则返回当前路径的数字和
class Solution
{
public:
int dfs(TreeNode* root, int presum)
{
if(!root) return 0;
int sum = presum*10 + root->val;
if(!root->left && !root->right) return sum;
return dfs(root->left, sum) + dfs(root->right, sum);
}
int sumNumbers(TreeNode* root)
{
return dfs(root, 0);
}
};
三、二叉树剪枝
思路:
- 每次先遍历左右子树进行剪枝(注意链接起来)
- 再判断当前结点是否为叶子节点,且值为0,若成立则删除该结点,返回空,否则返回该节点
- 终止条件:root为空,返回空
class Solution
{
public:
TreeNode* dfs(TreeNode* root)
{
if(!root) return nullptr;
root->left = dfs(root->left);
root->right = dfs(root->right);
if(!root->left && !root->right && root->val == 0) return nullptr;
else return root;
}
TreeNode* pruneTree(TreeNode* root)
{
return dfs(root);
}
};
四、验证搜索二叉树
思路:
- prev保存上一个结点的值,以便与当前结点进行比较
- 中序遍历,如果prev < root->val,则更新prev,否则返回false
- 剪枝:每次对当前情况判断,若不满足直接返回false,不用继续深搜判断
- 终止条件:如果root为空,则返回true
class Solution
{
public:
long long prev = LLONG_MIN;
bool dfs(TreeNode* root)
{
if(root == nullptr) return true;
if(!dfs(root->left)) return false;
if(prev < root->val) prev = root->val;
else return false;
if(!dfs(root->right)) return false;
return true;
}
bool isValidBST(TreeNode* root)
{
return dfs(root);
}
};
五、二叉搜索树中第k小的元素
思路:
- count计算访问结点次数,ret保存结果
- 中序遍历,每次–count,当count为0,则保存结果
- 终止条件:当root为空时,return;
- 剪枝:当count为空时,return;
class Solution
{
public:
int count = 0;
int ret = -1;
void dfs(TreeNode* root)
{
if(root == nullptr || count == 0) return;
dfs(root->left);
--count;
if(count == 0) ret = root->val;
dfs(root->right);
}
int kthSmallest(TreeNode* root, int k)
{
count = k;
dfs(root);
return ret;
}
};
六、二叉树的所有路径
思路:
- ret保存所有路径,path表示单条路径
- path设置为临时变量,以便回溯
- 先序遍历,若为叶子节点,则加上当前字符,并添加到ret中,若不为叶子节点,则加上当前字符和 “->”
- 终止条件:当root为空,return;
class Solution
{
vector<string> ret;
public:
void dfs(TreeNode* root, string path)
{
if(!root) return;
if(!root->left && !root->right)
{
path += to_string(root->val);
ret.push_back(path);
}
else path += to_string(root->val) + "->";
dfs(root->left, path);
dfs(root->right, path);
}
vector<string> binaryTreePaths(TreeNode* root)
{
dfs(root, "");
return ret;
}
};
总结
- 全局变量
- 在dfs中,全局变量非常好用(能拉全局就拉全局),它可以减少函数头设计的参数,简化函数体过程
- 但是有时使用临时变量更加合适,这种情况出现在“恢复现场”比较麻烦时,用临时变量反而能大大简化过程
- 回溯
- 回溯的过程中,要“恢复现场”
- 剪枝
- 即时进行判断,避免不必要的搜索