1、二叉搜索树
- 二叉搜索树是特殊的二叉树,他的左子节点总是小于或等于根节点,而右子节点总是大于或等于根节点
- 对二叉搜索树进行中序遍历,则遍历到的结点值是按照递增的顺序进行遍历的每个节点
[图片]
2、LCR 052. 递增顺序搜索树
题目信息:
- https://leetcode.cn/problems/NYBBNL/description/
给你一棵二叉搜索树,请 按中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,
并且每个节点没有左子节点,只有一个右子节点。
示例 1:
输入:root = [5,3,6,2,4,null,8,1,null,null,null,7,9]
输出:[1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]
示例 2:
输入:root = [5,1,7]
输出:[1,null,5,null,7]
提示:
树中节点数的取值范围是 [1, 100]
0 <= Node.val <= 1000
解题思路:
- 1、审题:输入一棵二叉搜索树,要求将该二叉搜索树转变成只有右子节点的递增顺序搜索树
- 2、解题:
- 二叉搜索树的定义是左子节点值全部小于根节点值,而右子结点值全部大于根节点值,左右子树也是一样的情况
- 要将原先的二叉树搜索树,转变成单条链的递增搜索树,意思是新的二叉树只有右子节点且还是二叉搜索树,则新的二叉树的根节点值最小,右子节点不断递增
- 采用中序遍历二叉搜索树,迭代代码方式,在遍历的过程中组装成新的递增搜索树
- 将中序遍历到的结点值,插入到新二叉搜索树的右子节点
代码实现:
TreeNode *increasingBST(TreeNode *root)
{
TreeNode *first = nullptr;
TreeNode *curr = root;
TreeNode *prev = nullptr;
std::stack<TreeNode *> stack;
while (curr != nullptr || !stack.empty())
{
while (curr != nullptr)
{
stack.push(curr);
curr = curr->left;
}
curr = stack.top();
stack.pop();
if (prev != nullptr)
{
prev->right = curr;
}
else
{
first = curr;
}
prev = curr;
prev->left = nullptr;
curr = curr->right;
}
return first;
}
3、LCR 053. 二叉搜索树中的中序后继
题目信息:
- https://leetcode.cn/problems/P5rCT8/description/
给定一棵二叉搜索树和其中的一个节点 p ,找到该节点在树中的中序后继。如果节点没有中序后继,请返回 null 。
节点 p 的后继是值比 p.val 大的节点中键值最小的节点,即按中序遍历的顺序节点 p 的下一个节点。
示例 1:
输入:root = [2,1,3], p = 1
输出:2
解释:这里 1 的中序后继是 2。请注意 p 和返回值都应是 TreeNode 类型。
示例 2:
输入:root = [5,3,6,2,4,null,null,1], p = 6
输出:null
解释:因为给出的节点没有中序后继,所以答案就返回 null 了。
提示:
树中节点的数目在范围 [1, 104] 内。
-105 <= Node.val <= 105
树中各节点的值均保证唯一。
解题思路:
- 1、审题:
- 输入一棵二叉搜索树,和其中一个节点p,要求按照中序遍历的顺序找到节点p的下一个节点,并返回,没有则返回null
- 2、解法1:
- 迭代方式遍历二叉搜索树,使用一个变量记录是否找到了与节点p相同的结点,如果找到了,则下一个节点就是目标值了
代码实现1:
TreeNode *inorderSuccessor(TreeNode *root, TreeNode *p)
{
std::stack<TreeNode *> stack;
TreeNode *curr = root;
bool found = false;
while (curr != nullptr || !stack.empty())
{
while (curr != nullptr)
{
stack.push(curr);
curr = curr->left;
}
curr = stack.top();
stack.pop();
if (found)
{
return curr;
}
if (curr->val == p->val)
{
found = true;
}
curr = curr->right;
}
return nullptr;
}
解法2:
- 解题2:二叉搜索树解法
- 根据二叉搜索树特性,左子树所有结点值比根节点值小,右子树所有节点值比根节点值大,
- 从根节点开始遍历,如果遍历到的当前节点值比目标节点p的值要小,则继续遍历大的右子树结点
- 如果遍历到的当前结点值比目标值大,则继续寻找他的左子节点,因为他的左子结点肯定也比当前结点值要大(如果有的话)
代码实现2:
TreeNode *inorderSuccessor(TreeNode *root, TreeNode *p)
{
TreeNode *result = nullptr;
TreeNode *curr = root;
while (curr != nullptr)
{
if (curr->val > p->val)
{
result = curr;
curr = curr->left;
}
else
{
curr = curr->right;
}
}
return result;
}
4、LCR 054. 把二叉搜索树转换为累加树
题目信息:
- https://leetcode.cn/problems/w6cpku/description/
给定一个二叉搜索树,请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。
提醒一下,二叉搜索树满足下列约束条件:
节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。
示例 1:
输入:root = [4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]
示例 2:
输入:root = [0,null,1]
输出:[1,null,1]
示例 3:
输入:root = [1,0,2]
输出:[3,3,2]
示例 4:
输入:root = [3,2,4,1]
输出:[7,9,4,10]
提示:
树中的节点数介于 0 和 104 之间。
每个节点的值介于 -104 和 104 之间。
树中的所有值 互不相同 。
给定的树为二叉搜索树。
解题思路:
- 1、审题:
- 输入一棵二叉搜索树,要求将该二叉树中的结点值替换为,比当前节点值大的所有结点的累加和
- 2、解题:
- 因为题目输入的是一个二叉搜索树,按照中序遍历得到的结点集合是升序排列的,那要找出比当前结点值大的结点,也就是中序遍历中该界面后面的所有结点
- 但是中序遍历是从左子树开始遍历的,可以将遍历顺序反过来,先遍历右子节点,接着遍历根节点,最后遍历左子结点,
- 在遍历过程中使用遍历sum获取节点累计和,并更新当前结点的值为sum即可
代码实现:
TreeNode *convertBST(TreeNode *root)
{
std::stack<TreeNode *> stack;
TreeNode *curr = root;
int sum = 0;
while (curr != nullptr || !stack.empty())
{
while (curr != nullptr)
{
stack.push(curr);
curr = curr->right;
}
curr = stack.top();
stack.pop();
sum += curr->val;
curr->val = sum;
curr = curr->left;
}
return root;
}
5、LCR 055. 二叉搜索树迭代器
题目信息:
- https://leetcode.cn/problems/kTOapQ/
实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:
BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
int next()将指针向右移动,然后返回指针处的数字。
注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。
可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。
示例:
输入:
inputs = ["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
inputs = [[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
输出:
[null, 3, 7, true, 9, true, 15, true, 20, false]
解释:
BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
bSTIterator.next();
bSTIterator.next();
bSTIterator.hasNext();
bSTIterator.next();
bSTIterator.hasNext();
bSTIterator.next();
bSTIterator.hasNext();
bSTIterator.next();
bSTIterator.hasNext();
提示:
树中节点的数目在范围 [1, 105] 内
0 <= Node.val <= 106
最多调用 105 次 hasNext 和 next 操作
进阶:
你可以设计一个满足下述条件的解决方案吗?next() 和 hasNext() 操作均摊时间复杂度为 O(1) ,并使用 O(h) 内存。其中 h 是树的高度。
解题思路:
- 1、审题:
-
- 输入一棵二叉搜索树,实现这棵二叉搜索的迭代器方法,也就是next和hasNext方法,用于判断这棵二叉搜索树是否有下一个节点和下一个节点的值
- 2、解题:
-
- 这个问题可以转化为二叉树的迭代中序遍历方法的,其中while的判断条件就是hasNext的的,不断从栈中取出树结点就是next方法的实现逻辑
代码实现:
class BSTIterator
{
public:
BSTIterator(TreeNode *root)
{
curr = root;
}
int next()
{
while (curr != nullptr)
{
stack.push(curr);
curr = curr->left;
}
curr = stack.top();
stack.pop();
int val = curr->val;
curr = curr->right;
return val;
}
bool hasNext()
{
return curr != nullptr || !stack.empty();
}
private:
std::stack<TreeNode *> stack;
TreeNode *curr = nullptr;
};
6、LCR 056. 两数之和 IV - 输入二叉搜索树
题目信息:
- https://leetcode.cn/problems/opLdQZ/description/
给定一个二叉搜索树的 根节点 root 和一个整数 k , 请判断该二叉搜索树中是否存在两个节点它们的值之和等于 k 。假设二叉搜索树中节点的值均唯一。
示例 1:
输入: root = [8,6,10,5,7,9,11], k = 12
输出: true
解释: 节点 5 和节点 7 之和等于 12
示例 2:
输入: root = [8,6,10,5,7,9,11], k = 22
输出: false
解释: 不存在两个节点值之和为 22 的节点
提示:
二叉树的节点个数的范围是 [1, 104].
-104 <= Node.val <= 104
root 为二叉搜索树
-105 <= k <= 105
解题思路:
- 1、审题:输入一棵二叉搜索树和目标值k,要求查找二叉树中是否存在两个节点的值之和等于目标值k,并返回结果
- 2、解题:
- 哈希表+中序遍历解法:中序遍历过程中使用哈希表map保存遍历到的节点值,并判断是否存在k-val的值,在之前是否存在
- 双指针+二分法查找:因为输入的二叉搜索树中序遍历是升序的,可采用二分查找方法,分别从二叉树的前后最大最小值节点开始遍历
- 如果两个节点值和等于目标值则返回,如果和大于目标值,则右侧最大节点值左移,否则左侧最小值右移
代码实现:
bool findTarget(TreeNode *root, int k)
{
std::vector<int> vector;
std::stack<TreeNode *> stack;
TreeNode *curr = root;
while (curr != nullptr || !stack.empty())
{
while (curr != nullptr)
{
stack.push(curr);
curr = curr->left;
}
curr = stack.top();
stack.pop();
if (find(vector.begin(), vector.end(), k - curr->val) != vector.end())
{
return true;
}
vector.push_back(curr->val);
curr = curr->right;
}
return false;
}
7、总结
- 二叉搜索树也是一颗二叉树,三种结点遍历的方式都适用
- 二叉搜索树的特点是左子节点都小于等于根节点值,右子节点值都大于等于根节点的值,使用中序遍历的方式遍历结点,则遍历到的结点是顺序排列的
- 二叉树的核心还是对树的遍历,特别是迭代方式实现的代码