原题链接🔗:二叉搜索树中第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 小的值,你将如何优化算法?
题解
二叉搜索树
二叉搜索树(Binary Search Tree,简称BST)是一种特殊的二叉树,它具有以下性质:
有序性:对于树中的每个节点,其左子树上所有节点的值都小于该节点的值,其右子树上所有节点的值都大于或等于该节点的值。
没有兄弟节点:每个节点最多只有一个左子节点和一个右子节点。
每个节点存储一个键值:在二叉搜索树中,每个节点通常存储一个键值,该键值用于维护树的有序性。
树结构:二叉搜索树是树结构,这意味着它是一个没有环的分层数据结构。
平衡性:理想情况下,二叉搜索树是平衡的,即左右子树的高度差不超过1。平衡的二叉搜索树可以保证操作(如搜索、插入和删除)的时间复杂度为O(log n)。但在最坏的情况下,如果插入的元素是有序的,树将退化成链表,时间复杂度变为O(n)。
二叉搜索树的应用非常广泛,因为它提供了高效的数据存储和检索方式。以下是一些基本操作:
搜索:在BST中搜索一个元素,可以从头节点开始,根据目标值与当前节点值的比较结果,决定是向左子树还是向右子树搜索。这个过程可以递归或迭代进行,直到找到目标值或到达叶子节点。
插入:向BST中插入一个新元素,首先搜索该元素应该插入的位置,然后根据BST的性质将其插入到适当的位置。
删除:从BST中删除一个元素,需要考虑几种情况:删除的节点没有子节点、有一个子节点或有两个子节点。在删除节点后,需要调整树以保持BST的性质。
遍历:BST可以通过前序、中序、后序和层序遍历来访问所有节点。中序遍历特别有用,因为它将按照升序访问所有节点。
二叉搜索树的实现通常涉及到递归和迭代技术,以及对树结构的深入理解。在实际应用中,为了提高性能,可能会使用自平衡的二叉搜索树,如AVL树或红黑树。
中序遍历
二叉树的中序遍历是一种遍历二叉树的方法,其遍历顺序为:先遍历左子树,然后访问根节点,最后遍历右子树。这种遍历方式可以确保在访问任何节点之前,其所有左子节点已经被访问过,同样地,在访问任何节点之后,其所有右子节点也会被访问。
中序遍历对于二叉搜索树(BST)特别有用,因为它会按照节点值的非递减顺序访问所有节点,即中序遍历的结果是一个有序数组。
以下是中序遍历的基本步骤:
访问左子树:首先,递归地对左子树进行中序遍历。
访问根节点:然后,访问根节点。在遍历过程中,通常会将根节点的值添加到一个列表中。
访问右子树:最后,递归地对右子树进行中序遍历。
中序遍历的时间复杂度为O(n),其中n是树中节点的数量,因为它需要访问树中的每个节点。空间复杂度取决于递归调用的深度,最坏情况下是O(n)(当树退化成链表时),最好情况下是O(log
n)(当树是平衡的)。
中序遍历递归法
- 解题思路:
在LeetCode上,题目“二叉搜索树中第K小的元素”通常要求你找到一个二叉搜索树(BST)中第K小的元素。二叉搜索树的性质是:对于树中的任何节点,其左子树上的所有节点的值都小于该节点的值,其右子树上的所有节点的值都大于该节点的值。
解题思路如下:
理解BST的性质:首先,要利用BST的性质来简化问题。在BST中,中序遍历(左-根-右)会以递增的顺序访问所有节点。
中序遍历:由于题目要求找到第K小的元素,我们可以通过中序遍历BST来实现。在遍历过程中,记录访问的节点数量。
计数与停止:在中序遍历的过程中,当访问到第K个节点时,停止遍历。这个节点就是所求的第K小的元素。
递归或迭代:中序遍历可以通过递归或迭代的方式实现。递归是更直观的方法,但迭代可以避免潜在的递归深度问题。
实现算法:编写代码实现上述逻辑。
- c++ demo:
#include <iostream>
#include <vector>
// 定义二叉树的节点结构
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
// 主函数,接收二叉搜索树的根节点和K值,返回第K小的元素
int kthSmallest(TreeNode* root, int k) {
std::vector<int> elements;
inOrderTraversal(root, elements, k);
return elements[k - 1]; // 由于数组索引从0开始,所以用k-1
}
private:
// 中序遍历辅助函数,同时接收一个vector来存储遍历结果
void inOrderTraversal(TreeNode* node, std::vector<int>& elements, int k) {
if (!node || elements.size() >= k) {
return;
}
// 遍历左子树
inOrderTraversal(node->left, elements, k);
// 访问当前节点
if (elements.size() < k) {
elements.push_back(node->val);
}
// 遍历右子树
inOrderTraversal(node->right, elements, k);
}
};
// 示例使用
int main() {
// 构建一个示例二叉搜索树
// 3
// / \
// 1 4
// \
// 2
TreeNode* root = new TreeNode(3);
root->left = new TreeNode(1);
root->right = new TreeNode(4);
root->left->right = new TreeNode(2);
Solution solution;
int k = 3; // 假设我们要找第3小的元素
std::cout << "The " << k << "st smallest element is: " << solution.kthSmallest(root, k) << std::endl;
// 清理分配的内存(在实际应用中应该使用智能指针来自动管理内存)
delete root->left->right;
delete root->left;
delete root->right;
delete root;
return 0;
}
- 输出结果:
The 3st smallest element is: 3
- 代码仓库地址:kthSmallest