- 96. 不同的二叉搜索树">96. 不同的二叉搜索树
- 108. 将有序数组转换为二叉搜索树">108. 将有序数组转换为二叉搜索树
- 109. 有序链表转换二叉搜索树">109. 有序链表转换二叉搜索树
- 95. 不同的二叉搜索树 II">95. 不同的二叉搜索树 II
- 剑指 Offer 36. 二叉搜索树与双向链表">剑指 Offer 36. 二叉搜索树与双向链表
- 98. 验证二叉搜索树(判断一棵树是否为二叉搜索树)">98. 验证二叉搜索树(判断一棵树是否为二叉搜索树)
- 173. 二叉搜索树迭代器">173. 二叉搜索树迭代器
- 剑指 Offer II 053. 二叉搜索树中的中序后继">剑指 Offer II 053. 二叉搜索树中的中序后继
- 普通二叉树的中序后继
96. 不同的二叉搜索树
给你一个整数 n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入:n = 3
输出:5
示例 2:
输入:n = 1
输出:1
提示:
1 <= n <= 19
思路:
线性DP
class Solution {
public int numTrees(int n) {
int[] f = new int[n + 1];
f[0] = f[1] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= i; j++) {
f[i] += f[j - 1] * f[i - j];
}
}
return f[n];
}
}
108. 将有序数组转换为二叉搜索树
给你一个整数数组 nums
,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
示例 1:
输入:nums = [-10,-3,0,5,9]输出:[0,-3,9,-10,null,5]解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
示例 2:
输入:nums = [1,3]
输出:[3,1]
解释:[1,3] 和 [3,1] 都是高度平衡二叉搜索树。
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
按 严格递增 顺序排列
思路:递归自上而下建立整棵树即可!
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
int n = nums.length;
if (n == 0)
return null;
TreeNode root = new TreeNode(nums[n / 2]);
root.left = dfs(nums, 0, n / 2 - 1);
root.right = dfs(nums, n / 2 + 1, n - 1);
return root;
}
TreeNode dfs(int[] nums, int l, int r) {
if (l > r)
return null;
int mid = l + r >> 1;
TreeNode cur = new TreeNode(nums[mid]);
cur.left = dfs(nums, l, mid - 1);
cur.right = dfs(nums, mid + 1, r);
return cur;
}
}
109. 有序链表转换二叉搜索树
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
思路:
- 遍历一遍链表得到长度
- 用108的方法建一棵空树
- 中序遍历填充每个节点
巧妙选择遍历方案避免链表不能随机访问的缺点!
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
ListNode gg;
public TreeNode sortedListToBST(ListNode head) {
ListNode cur = head;
gg = head;
int len = 0;
while (cur != null) {
cur = cur.next;
len++;
}
// System.out.println(len);
TreeNode root = dfs1(0, len - 1);
dfs2(root);
return root;
}
TreeNode dfs1(int l, int r) {
if (l > r)
return null;
int mid = l + r >> 1;
TreeNode cur = new TreeNode();
cur.left = dfs1(l, mid - 1);
cur.right = dfs1(mid + 1, r);
return cur;
}
void dfs2(TreeNode root) {
if (root == null)
return;
dfs2(root.left);
root.val = gg.val;
gg = gg.next;
dfs2(root.right);
}
}
95. 不同的二叉搜索树 II
难度中等1024收藏分享切换为英文接收动态反馈
给你一个整数 n
,请你生成并返回所有由 n
个节点组成且节点值从 1
到 n
互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。
示例 1:
输入:n = 3
输出:[[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]
示例 2:
输入:n = 1
输出:[[1]]
提示:
1 <= n <= 8
思路:
在96的基础上,不问个数,而是要求输出每一种情况的根节点构成的链表
也用到了108的类似转换方法
我当时的问题:怎么递归保存所有的结果?
是我太年轻了,还是得再暴力一点才能做出来
对于每个根节点来说,生成它的左右子树的所有情况并做拼接
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<TreeNode> generateTrees(int n) {
return dfs(1, n);
}
List<TreeNode> dfs(int l, int r) {
if (l > r) {
List<TreeNode> res = new ArrayList<>();
res.add(null);
return res;
}
List<TreeNode> res = new ArrayList<>();
for (int i = l; i <= r; i++) {
List<TreeNode> ll = dfs(l, i - 1);
List<TreeNode> rr = dfs(i + 1, r);
for (TreeNode lo : ll) {
for (TreeNode ro : rr) {
TreeNode root = new TreeNode(i, lo, ro);
res.add(root);
}
}
}
return res;
}
}
剑指 Offer 36. 二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
思路:
先序遍历,不要被二叉搜索树的中序遍历给干扰!
递归得到当前根节点左右子树的双向链表,再将根节点合并到双向链表中!
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val,Node _left,Node _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public Node treeToDoublyList(Node root) {
if (root == null)
return null;
Pair p = dfs(root);
p.left.left = p.right;
p.right.right = p.left;
return p.left;
}
Pair dfs(Node root) {
if (root.left == null && root.right == null)
return new Pair(root, root);
else if (root.left != null && root.right != null) {
Pair p = dfs(root.left);
Pair q = dfs(root.right);
p.right.right = root;
root.left = p.right;
q.left.left = root;
root.right = q.left;
return new Pair(p.left, q.right);
} else if (root.left != null) {
Pair p = dfs(root.left);
p.right.right = root;
root.left = p.right;
return new Pair(p.left, root);
} else {
Pair q = dfs(root.right);
q.left.left = root;
root.right = q.left;
return new Pair(root, q.right);
}
}
}
class Pair {
Node left, right;
Pair(Node left, Node right) {
this.left = left;
this.right = right;
}
}
98. 验证二叉搜索树(判断一棵树是否为二叉搜索树)
给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 小于 当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3]
输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。
提示:
- 树中节点数目范围在
[1, 104]
内 -231 <= Node.val <= 231 - 1
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
long min = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
return dfs(root);
}
boolean dfs(TreeNode root) {
if (root == null)
return true;
if (!dfs(root.left))
return false;
if (root.val <= min)
return false;
min = root.val;
return dfs(root.right);
}
}
173. 二叉搜索树迭代器
实现一个二叉搜索树迭代器类BSTIterator
,表示一个按中序遍历二叉搜索树(BST)的迭代器:
BSTIterator(TreeNode root)
初始化BSTIterator
类的一个对象。BST 的根节点root
会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。boolean hasNext()
如果向指针右侧遍历存在数字,则返回true
;否则返回false
。int next()
将指针向右移动,然后返回指针处的数字。
注意,指针初始化为一个不存在于 BST 中的数字,所以对 next()
的首次调用将返回 BST 中的最小元素。
你可以假设 next()
调用总是有效的,也就是说,当调用 next()
时,BST 的中序遍历中至少存在一个下一个数字。
示例:
输入
[“BSTIterator”, “next”, “next”, “hasNext”, “next”, “hasNext”, “next”, “hasNext”, “next”, “hasNext”]
[[[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(); // 返回 3
bSTIterator.next(); // 返回 7
bSTIterator.hasNext(); // 返回 True
bSTIterator.next(); // 返回 9
bSTIterator.hasNext(); // 返回 True
bSTIterator.next(); // 返回 15
bSTIterator.hasNext(); // 返回 True
bSTIterator.next(); // 返回 20
bSTIterator.hasNext(); // 返回 False
提示:
- 树中节点的数目在范围
[1, 105]
内 0 <= Node.val <= 106
最多调用
105
次hasNext
和next
操作
进阶:你可以设计一个满足下述条件的解决方案吗?
next()
和hasNext()
操作均摊时间复杂度为O(1)
,并使用O(h)
内存。其中h
是树的高度。
思路:
使用自定义栈进行中序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class BSTIterator {
Deque<TreeNode> stack = new LinkedList<>();
public BSTIterator(TreeNode root) {
while (root != null) {
stack.push(root);
root = root.left;
}
}
public int next() {
TreeNode cur = stack.pop();
int x = cur.val;
if (cur.right != null) {
cur = cur.right;
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
}
return x;
}
public boolean hasNext() {
return !stack.isEmpty();
}
}
/**
* Your BSTIterator object will be instantiated and called as such:
* BSTIterator obj = new BSTIterator(root);
* int param_1 = obj.next();
* boolean param_2 = obj.hasNext();
*/
剑指 Offer II 053. 二叉搜索树中的中序后继
给定一棵二叉搜索树和其中的一个节点 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
- 树中各节点的值均保证唯一。
思路:
因为是二叉搜索树,所以给定节点的中序后继就是第一个比它节点存的值大的节点。
只需扫描目标节点所在的分支(指一条完整的从根节点到叶节点的路径,其中包括目标节点,如果存在的话)即可找到第一个比他大的节点。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
TreeNode ans = null, cur = root;
while (cur != null) {
if (cur.val > p.val) {
ans = cur;
cur = cur.left;
} else {
cur = cur.right;
}
}
return ans;
}
}
普通二叉树的中序后继
思路:
利用中序遍历,记录目标节点是否已经被访问到,若目标节点已经被访问到,则接下来遍历的第一个节点就是其后继
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
TreeNode ans;
boolean flag;
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
dfs(root, p);
return ans;
}
void dfs(TreeNode root, TreeNode p) {
if (root == null) return;
dfs(root.left, p);
if (flag && ans == null)
ans = root;
if (root == p)
flag = true;
dfs(root.right, p);
}
}