二叉树的深度优先搜索是一种遍历二叉树的方法,它通过深度递归的方式探索树的结构。有两种主要形式:前序遍历、中序遍历、和后序遍历。在二叉树上执行深度优先搜索,意味着首先探索到树的深度,然后回溯到更浅的层次。
以下是三种常见的二叉树深度优先搜索方式的定义:
前序遍历(Preorder Traversal):
在实现深度优先搜索时,通常使用递归的方法,递归函数的调用栈会自动帮助我们在深度上进行探索。对于每个节点,首先处理当前节点,然后递归处理其左子树和右子树。
示例代码(C++):
struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; void preorderTraversal(TreeNode* root) { if (root == nullptr) return; // 处理当前节点 cout << root->val << " "; // 递归处理左子树 preorderTraversal(root->left); // 递归处理右子树 preorderTraversal(root->right); } void inorderTraversal(TreeNode* root) { if (root == nullptr) return; // 递归处理左子树 inorderTraversal(root->left); // 处理当前节点 cout << root->val << " "; // 递归处理右子树 inorderTraversal(root->right); } void postorderTraversal(TreeNode* root) { if (root == nullptr) return; // 递归处理左子树 postorderTraversal(root->left); // 递归处理右子树 postorderTraversal(root->right); // 处理当前节点 cout << root->val << " "; }
这些函数分别实现了二叉树的前序、中序和后序深度优先搜索。在实际应用中,可以根据问题的要求选择不同的遍历方式。
题目链接:https://leetcode.cn/problems/evaluate-boolean-binary-tree/
给你一棵 完整二叉树 的根,这棵树有以下特征:
计算 一个节点的值方式如下:
返回根节点 root 的布尔运算值。
完整二叉树 是每个节点有 0 个或者 2 个孩子的二叉树。
叶子节点 是没有孩子的节点。
示例 1:
输入:root = [2,1,3,null,null,0,1] 输出:true 解释:上图展示了计算过程。 AND 与运算节点的值为 False AND True = False 。 OR 运算节点的值为 True OR False = True 。 根节点的值为 True ,所以我们返回 true 。
示例 2:
输入:root = [0] 输出:false 解释:根节点是叶子节点,且值为 false,所以我们返回 false 。
提示:
思路
二叉树的大部分题目都可划分为子问题,所以一般我们都使用递归来解决,这里我们要先访问左右节点再访问根节点进行运算,所以这里使用的应该是后序遍历来写递归函数。
代码
class Solution { public: bool evaluateTree(TreeNode* root) { if(!root->left) return root->val; int left = evaluateTree(root->left); int right = evaluateTree(root->right); return root->val==2?left|right:left&right; } };
题目链接:https://leetcode.cn/problems/sum-root-to-leaf-numbers/
给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:
计算从根节点到叶节点生成的 所有数字之和 。
叶节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3] 输出:25 解释: 从根到叶子节点路径 1->2 代表数字 12 从根到叶子节点路径 1->3 代表数字 13 因此,数字总和 = 12 + 13 = 25
示例 2:
输入:root = [4,9,0,5,1] 输出:1026 解释: 从根到叶子节点路径 4->9->5 代表数字 495 从根到叶子节点路径 4->9->1 代表数字 491 从根到叶子节点路径 4->0 代表数字 40 因此,数字总和 = 495 + 491 + 40 = 1026
提示:
思路
在前序遍历过程中,我们能够通过向左右子树传递信息,并在回溯时获取左右子树的返回值。递归函数的作用主要体现在两个方面:
代码
class Solution { public: int sumNumbers(TreeNode* root) { return dfs(root,0); } int dfs(TreeNode* root,int pre){ int presum = pre*10+root->val; if(!root->left&&!root->right) return presum; int ret=0; if(root->left) ret+=dfs(root->left,presum); if(root->right) ret+=dfs(root->right,presum); return ret; } };
题目链接:https://leetcode.cn/problems/binary-tree-pruning/
给你二叉树的根结点 root ,此外树的每个结点的值要么是 0 ,要么是 1 。
返回移除了所有不包含 1 的子树的原二叉树。
节点 node 的子树为 node 本身加上所有 node 的后代。
示例 1:
输入:root = [1,null,0,0,1] 输出:[1,null,0,null,1] 解释: 只有红色节点满足条件“所有不包含 1 的子树”。 右图为返回的答案。
示例 2:
输入:root = [1,0,1,0,0,0,1] 输出:[1,null,1,null,1]
示例 3:
输入:root = [1,1,0,1,1,0,1,0] 输出:[1,1,0,1,1,null,1]
提示:
思路
这里我们可以通过题目可以,我们要先考虑左右子树才能判断当前节点是否能够删除,所以这里我们采用后序遍历方式来使用递归解决此问题
代码
class Solution { public: TreeNode* pruneTree(TreeNode* root) { if(!root) return nullptr; root->left=pruneTree(root->left); root->right=pruneTree(root->right); if(!root->left&&!root->right&&root->val==0){ delete root; root=nullptr; } return root; } };
题目链接:https://leetcode.cn/problems/validate-binary-search-tree/
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
示例 1:
输入:root = [2,1,3] 输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6] 输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。
提示:
思路
这里我们可以采用二叉搜索树的一个性质,即中序遍历后为一个有序序列,所以我们只需要中序遍历二叉树时判断当前节点是否比前一个节点大即可,在遍历左子树和当前节点时,不符合直接返回false可以剪去不必要遍历的枝。
代码
class Solution { long prev=LONG_MIN; public: bool isValidBST(TreeNode* root) { if(!root) return true; bool left = isValidBST(root->left); if(!left) return false; bool cur=false; if(root->val>prev) cur=true; if(!cur) return false; prev=root->val; bool right =isValidBST(root->right); return left&&right&&cur; } };
long prev = LONG_MIN;: 这个类成员变量用于追踪上一个访问的节点的值,初始值设置为LONG_MIN,即负无穷,以确保对根节点的验证不会出现问题。
bool isValidBST(TreeNode* root): 这是主要的函数,用于验证二叉搜索树是否合法。
题目链接:https://leetcode.cn/problems/kth-smallest-element-in-a-bst/
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。
示例 1:
输入:root = [3,1,4,null,2], k = 1 输出:1
示例 2:
输入:root = [5,3,6,2,4,null,null,1], k = 3 输出:3
提示:
**进阶:**如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?
思路
根据上面的题目我们不难想到使用中序遍历和计数的方式来找到第k小的值,并加上计数器的剪枝优化。
代码
class Solution { int count,ret; public: int kthSmallest(TreeNode* root, int k) { count=k; dfs(root); return ret; } void dfs(TreeNode* root){ if(root==nullptr||!count) return; dfs(root->left); count--; if(!count) ret=root->val; dfs(root->right); } };
题目链接:https://leetcode.cn/problems/binary-tree-paths/
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5] 输出:["1->2->5","1->3"]
示例 2:
输入:root = [1] 输出:["1"]
提示:
思路
使用深度优先遍历(DFS)来解决问题。路径以字符串形式存储,从根节点开始遍历,每次遍历时将当前节点的值加入到路径中。如果该节点为叶子节点,将路径存储到结果中。否则,将"->"加入到路径中并递归遍历该节点的左右子树。定义一个结果数组,在递归过程中进行路径的构建和存储。具体递归实现方法如下:
需要注意的是这里我们传入path时,不传引用,否则回溯需要另外处理。
代码
class Solution { vectorret; public: vector binaryTreePaths(TreeNode* root) { string path; dfs(root,path); return ret; } void dfs(TreeNode* root,string path){ path += to_string(root->val); if(!root->left&&!root->right){ ret.push_back(path); return; } path+="->"; if(root->left) dfs(root->left,path); if(root->right) dfs(root->right,path); } };