文章目录
- [230. 二叉搜索树中第K小的元素](#230. 二叉搜索树中第K小的元素)
- [解题思路:中序遍历 + 剪枝](#解题思路:中序遍历 + 剪枝)
- [257. 二叉树的所有路径](#257. 二叉树的所有路径)
- 解题思路:前序遍历
230. 二叉搜索树中第K小的元素
给定一个二叉搜索树的根节点 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
提示:
- 树中的节点数为
n
。 1 <= k <= n <= 104
0 <= Node.val <= 104
进阶: 如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k
小的值,你将如何优化算法?
解题思路:中序遍历 + 剪枝
其实这道题就是利用二叉搜索树的中序遍历之后得到有序序列的特性,所以我们只需要通过中序遍历来遍历一下二叉树,然后用一个全局变量或者成员变量 ret
来记录下第 k
个节点,至于如何判断是第 k
个节点,我们也可以搞一个全局变量 count
,初始值就是题目传入的 k
,然后每遍历一个节点进行中序处理的时候就让 count--
,然后判断一下 count
是否为零,是的话说明此时已经是要找的节点了,则直接返回即可!
此外,我们也可以做个剪枝操作,因为拿到第 k
个节点之后,后面的遍历就无意义了,所以我们只需要在递归函数出口添加一个判断,判断如果 count
为零的话,说明前面已经找到了第 k
个节点了,则直接返回,减少递归调用开销!
cpp
/**
* 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 {
private:
int count; // 初始化为最小的第k个数,每次遍历一个节点就进行减减
int ret; // 最终的返回结果
public:
int kthSmallest(TreeNode* root, int k) {
count = k;
ret = 0;
dfs(root);
return ret;
}
void dfs(TreeNode* root)
{
// 递归函数出口(顺便判断count,进行剪枝操作)
if(root == nullptr || count == 0)
return;
// 中序遍历,先递归到最左孩子
dfs(root->left);
// 然后处理当前节点,如果--count减为0了直接返回
if(--count == 0)
{
ret = root->val;
return;
}
// 最后处理右孩子
dfs(root->right);
}
};
257. 二叉树的所有路径
给你一个二叉树的根节点 root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
示例 2:
输入:root = [1]
输出:["1"]
提示:
- 树中节点的数目在范围
[1, 100]
内 -100 <= Node.val <= 100
解题思路:前序遍历
这道题并不难,无非就是遍历所有的从根节点到叶子节点的路径,只不过我们要将路径中的节点记录下来,所以我们可以考虑使用一个全局变量 ret
来记录每个从根节点到叶子节点的路径,然后用 path
变量来作为局部变量进行传参,方便回溯的时候自动"恢复现场"!
这里的回溯时候自动"恢复现场"的意思,其实就是因为当我们遍历完一条路径之后,我们肯定需要返回到上一层节点,继续看看它的右孩子还是继续返回上一层看看是否存在其它路径,那么此时 path
变量中记录的路径在返回的时候就得退回到原来的样子,为了方便,我们直接将其作为局部变量来传参,那么每一层调用的 path 变量其实都是独立的,就节省了回溯时候"恢复现场"的功夫,也就是如果设为全局变量的话,还得我们控制 path
当前该层的元素弹出,这就比较麻烦!
其它的没有什么好说的,因为要先处理当前节点,所以就是一个 前序遍历!然后函数头的话,按照上面的说法,就是下面这样子:
cpp
// path表示当前的路径情况,为局部变量,方便回溯的时候自动"恢复现场"
// ret表示路径结果集,为引用,这样子起到全局变量的作用
void dfs(TreeNode* root, string path, vector<string>& ret);
剩下的也比较简单,就是当叶子节点的时候我们就处理一下 ret
,将 path
以及当前叶子节点的路径字符串添加到 ret
中即可!如果不是叶子节点的话,则将当前节点添加到 path
中,继续往下递归左右子树即可!
完整代码如下:
cpp
/**
* 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<string> binaryTreePaths(TreeNode* root) {
vector<string> ret;
dfs(root, "", ret);
return ret;
}
// path为局部变量,方便回溯的时候自动"恢复现场"
// ret为引用,这样子起到全局变量的作用
void dfs(TreeNode* root, string path, vector<string>& ret)
{
// 函数递归出口
if(root->left == nullptr && root->right == nullptr)
{
ret.push_back(path + to_string(root->val));
return;
}
// 前序遍历,先处理当前节点
path += to_string(root->val) + "->";
// 再递归左右子树
if(root->left)
dfs(root->left, path, ret);
if(root->right)
dfs(root->right, path, ret);
}
};