LeetCode103 二叉树的锯齿形层序遍历
题目详情
给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]
示例 2:
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
提示:
树中节点数目在范围 [0, 2000] 内
-100 <= Node.val <= 100
代码
class LeetCode103 {
public static void main(String[] args) {
System.out.println(new Solution().zigzagLevelOrder(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
System.out.println(new Solution2().zigzagLevelOrder(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
System.out.println(new Solution3().zigzagLevelOrder(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
}
/*
算法
实现 BFS 的几种算法。
使用两层嵌套循环。外层循环迭代树的层级,内层循环迭代每层上的节点。
也可以使用一层循环实现 BFS。将要访问的节点添加到队列中,使用 分隔符(例如:空节点)把不同层的节点分隔开。分隔符表示一层结束和新一层开始。
这里采用第二种方法。在此算法的基础上,借助双端队列实现锯齿形顺序。在每一层,使用一个空的双端队列保存该层所有的节点。根据每一层的访问顺序,即从左到右或从右到左,决定从双端队列的哪一端插入节点。
*/
static class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
if (root == null) {
return new ArrayList<>();
}
List<List<Integer>> results = new ArrayList<>();
//添加带有分隔符的根元素以启动 BFS 循环
LinkedList<TreeNode> node_queue = new LinkedList<>();
node_queue.addLast(root);
node_queue.addLast(null);
LinkedList<Integer> level_list = new LinkedList<>();
boolean is_order_left = true;
while (node_queue.size() > 0) {
TreeNode curr_node = node_queue.pollFirst();
if (curr_node != null) {
if (is_order_left) {
level_list.addLast(curr_node.val);
} else {
level_list.addFirst(curr_node.val);
}
if (curr_node.left != null) {
node_queue.addLast(curr_node.left);
}
if (curr_node.right != null) {
node_queue.addLast(curr_node.right);
}
} else {
//完成了一层的扫描
results.add(level_list);
level_list = new LinkedList<>();
//为下一层做准备
if (node_queue.size() > 0)
node_queue.addLast(null);
is_order_left = !is_order_left;
}
}
return results;
}
}
/*
方法一:广度优先遍历
此题是「102. 二叉树的层序遍历」的变种,最后输出的要求有所变化,要求我们按层数的奇偶来决定每一层的输出顺序。
规定二叉树的根节点为第 00 层,如果当前层数是偶数,从左至右输出当前层的节点值,否则,从右至左输出当前层的节点值。
我们依然可以沿用第 102 题的思想,修改广度优先搜索,对树进行逐层遍历,用队列维护当前层的所有元素,当队列不为空的时候,
求得当前队列的长度 \textit{size}size,每次从队列中取出 \textit{size}size 个元素进行拓展,然后进行下一次迭代。
*/
static class Solution2 {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> ans = new LinkedList<List<Integer>>();
if (root == null) {
return ans;
}
Queue<TreeNode> nodeQueue = new ArrayDeque<TreeNode>();
nodeQueue.offer(root);
boolean isOrderLeft = true;
while (!nodeQueue.isEmpty()) {
Deque<Integer> levelList = new LinkedList<Integer>();
int size = nodeQueue.size();
for (int i = 0; i < size; ++i) {
TreeNode curNode = nodeQueue.poll();
if (isOrderLeft) {
levelList.offerLast(curNode.val);
} else {
levelList.offerFirst(curNode.val);
}
if (curNode.left != null) {
nodeQueue.offer(curNode.left);
}
if (curNode.right != null) {
nodeQueue.offer(curNode.right);
}
}
ans.add(new LinkedList<Integer>(levelList));
isOrderLeft = !isOrderLeft;
}
return ans;
}
}
/*
递归实现-深度优先遍历
- 相同层序的节点归入同一个数组
- 传入辅助的level参数,决定层序
*/
static class Solution3 {
private void dfs(TreeNode node, int level, List<List<Integer>> res) {
if (level == res.size()) {
LinkedList<Integer> newLevel = new LinkedList<Integer>();
newLevel.add(node.val);
res.add(newLevel);
} else {
if (level % 2 == 0) {
res.get(level).add(node.val);
} else {
res.get(level).add(0, node.val);
}
}
if (node.left != null) {
dfs(node.left, level + 1, res);
}
if (node.right != null) {
dfs(node.right, level + 1, res);
}
}
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
dfs(root, 0, res);
return res;
}
}
}
LeetCode106 从中序与后序遍历序列构造二叉树
题目详情
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例 2:
输入:inorder = [-1], postorder = [-1]
输出:[-1]
提示:
1 <= inorder.length <= 3000
postorder.length == inorder.length
-3000 <= inorder[i], postorder[i] <= 3000
inorder 和 postorder 都由 不同 的值组成
postorder 中每一个值都在 inorder 中
inorder 保证是树的中序遍历
postorder 保证是树的后序遍历
代码
class LeetCode106 {
public static void main(String[] args) {
TreeNode.prettyPrintTree(new Solution().buildTree(new int[]{9, 3, 15, 20, 7}, new int[]{9, 15, 7, 20, 3}));
TreeNode.prettyPrintTree(new Solution2().buildTree(new int[]{9, 3, 15, 20, 7}, new int[]{9, 15, 7, 20, 3}));
}
/*
递归
*/
static class Solution {
int post_idx;
int[] postorder;
int[] inorder;
Map<Integer, Integer> idx_map = new HashMap<Integer, Integer>();
public TreeNode helper(int in_left, int in_right) {
// 如果这里没有节点构造二叉树了,就结束
if (in_left > in_right) {
return null;
}
// 选择 post_idx 位置的元素作为当前子树根节点
int root_val = postorder[post_idx];
TreeNode root = new TreeNode(root_val);
// 根据 root 所在位置分成左右两棵子树
int index = idx_map.get(root_val);
// 下标减一
post_idx--;
// 构造右子树
root.right = helper(index + 1, in_right);
// 构造左子树
root.left = helper(in_left, index - 1);
return root;
}
public TreeNode buildTree(int[] inorder, int[] postorder) {
this.postorder = postorder;
this.inorder = inorder;
// 从后序遍历的最后一个元素开始
post_idx = postorder.length - 1;
// 建立(元素,下标)键值对的哈希表
int idx = 0;
for (Integer val : inorder) {
idx_map.put(val, idx++);
}
return helper(0, inorder.length - 1);
}
}
/*
迭代
*/
static class Solution2 {
public TreeNode buildTree(int[] inorder, int[] postorder) {
if (postorder == null || postorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(postorder[postorder.length - 1]);
Deque<TreeNode> stack = new LinkedList<TreeNode>();
stack.push(root);
int inorderIndex = inorder.length - 1;
for (int i = postorder.length - 2; i >= 0; i--) {
int postorderVal = postorder[i];
TreeNode node = stack.peek();
if (node.val != inorder[inorderIndex]) {
node.right = new TreeNode(postorderVal);
stack.push(node.right);
} else {
while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
node = stack.pop();
inorderIndex--;
}
node.left = new TreeNode(postorderVal);
stack.push(node.left);
}
}
return root;
}
}
}
LeetCode108 将有序数组转换为二叉树
题目详情
给你一个整数数组 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,null,3] 和 [3,1] 都是高度平衡二叉搜索树。
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 按 严格递增 顺序排列
代码
class LeetCode108 {
public static void main(String[] args) {
TreeNode.prettyPrintTree(new Solution().sortedArrayToBST(new int[]{-10, -3, 0, 5, 9}));
TreeNode.prettyPrintTree(new Solution2().sortedArrayToBST(new int[]{-10, -3, 0, 5, 9}));
TreeNode.prettyPrintTree(new Solution3().sortedArrayToBST(new int[]{-10, -3, 0, 5, 9}));
}
/*
方法一:中序遍历,总是选择中间位置左边的数字作为根节点
*/
static class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return helper(nums, 0, nums.length - 1);
}
public TreeNode helper(int[] nums, int left, int right) {
if (left > right) {
return null;
}
// 总是选择中间位置左边的数字作为根节点
int mid = (left + right) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = helper(nums, left, mid - 1);
root.right = helper(nums, mid + 1, right);
return root;
}
}
/*
方法二:中序遍历,总是选择中间位置右边的数字作为根节点
*/
static class Solution2 {
public TreeNode sortedArrayToBST(int[] nums) {
return helper(nums, 0, nums.length - 1);
}
public TreeNode helper(int[] nums, int left, int right) {
if (left > right) {
return null;
}
// 总是选择中间位置右边的数字作为根节点
int mid = (left + right + 1) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = helper(nums, left, mid - 1);
root.right = helper(nums, mid + 1, right);
return root;
}
}
/*
方法三:中序遍历,选择任意一个中间位置数字作为根节点
*/
static class Solution3 {
Random rand = new Random();
public TreeNode sortedArrayToBST(int[] nums) {
return helper(nums, 0, nums.length - 1);
}
public TreeNode helper(int[] nums, int left, int right) {
if (left > right) {
return null;
}
// 选择任意一个中间位置数字作为根节点
int mid = (left + right + rand.nextInt(2)) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = helper(nums, left, mid - 1);
root.right = helper(nums, mid + 1, right);
return root;
}
}
}
LeetCode110 平衡二叉树
题目详情
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:true
示例 2:
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例 3:
输入:root = []
输出:true
提示:
树中的节点数在范围 [0, 5000] 内
-104 <= Node.val <= 104
代码
class LeetCode110 {
public static void main(String[] args) {
System.out.println(new Solution().isBalanced(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
System.out.println(new Solution2().isBalanced(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
}
/*
方法一:自顶向下的递归
*/
static class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
} else {
return Math.abs(height(root.left) - height(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
}
}
public int height(TreeNode root) {
if (root == null) {
return 0;
} else {
return Math.max(height(root.left), height(root.right)) + 1;
}
}
}
/*
方法二:自底向上的递归
*/
static class Solution2 {
public boolean isBalanced(TreeNode root) {
return height(root) >= 0;
}
public int height(TreeNode root) {
if (root == null) {
return 0;
}
int leftHeight = height(root.left);
int rightHeight = height(root.right);
if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1) {
return -1;
} else {
return Math.max(leftHeight, rightHeight) + 1;
}
}
}
}
LeetCode112 路径总和
题目详情
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
示例 2:
输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。
示例 3:
输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。
提示:
树中节点的数目在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
代码
class LeetCode112 {
public static void main(String[] args) {
System.out.println(new Solution().hasPathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, 1}), 22));
System.out.println(new Solution2().hasPathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, 1}), 22));
}
/*
方法一:广度优先搜索
首先我们可以想到使用广度优先搜索的方式,记录从根节点到当前节点的路径和,以防止重复计算。
这样我们使用两个队列,分别存储将要遍历的节点,以及根节点到这些节点的路径和即可。
*/
static class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null) {
return false;
}
Queue<TreeNode> queNode = new LinkedList<TreeNode>();
Queue<Integer> queVal = new LinkedList<Integer>();
queNode.offer(root);
queVal.offer(root.val);
while (!queNode.isEmpty()) {
TreeNode now = queNode.poll();
int temp = queVal.poll();
if (now.left == null && now.right == null) {
if (temp == sum) {
return true;
}
continue;
}
if (now.left != null) {
queNode.offer(now.left);
queVal.offer(now.left.val + temp);
}
if (now.right != null) {
queNode.offer(now.right);
queVal.offer(now.right.val + temp);
}
}
return false;
}
}
/*
方法二:递归
*/
static class Solution2 {
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null) {
return false;
}
if (root.left == null && root.right == null) {
return sum == root.val;
}
return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}
}
}
LeetCode113 路径总和-二
题目详情
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
示例 2:
输入:root = [1,2,3], targetSum = 5
输出:[]
示例 3:
输入:root = [1,2], targetSum = 0
输出:[]
提示:
树中节点总数在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
代码
class LeetCode113 {
public static void main(String[] args) {
System.out.println(new Solution().pathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1}), 22));
System.out.println(new Solution2().dfs(TreeNode.deserialize("[5,4,8,11,null,13,4,7,2,null,null,5,1]"), 22));
System.out.println(new Solution3().pathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1}), 22));
System.out.println(new Solution4().pathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1}), 22));
}
/*
这一题是很典型的回溯算法
思路:
从根节点出发,使用一个temp变量和List容器记录走过路径
在走该条路径之前将节点的值加入List容器,走完之后(返回之后)将加入的值移除
当节点为空时,返回继续查找
当节点的左孩子和右孩子都为空并且temp变量等于sum时,说明这个节点是叶子节点且找到了一条路径,将List加入答案中
*/
static class Solution {
public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
pathSum(ans, path, root, 0, sum);
return ans;
}
public void pathSum(List<List<Integer>> ans, List<Integer> path, TreeNode root, int temp, int sum) {
if (root == null) {
return;
}
//叶子节点 且 匹配
if (root.left == null && root.right == null && temp + root.val == sum) {
path.add(root.val);/*添加该叶子节点*/
ans.add(new ArrayList<>(path));//加入结果数组
path.remove(path.size() - 1);//删除最后一项 相当与恢复到叶子节点上一层位置
return;
}
path.add(root.val);/*添加该节点*/
pathSum(ans, path, root.left, temp + root.val, sum);
pathSum(ans, path, root.right, temp + root.val, sum);
path.remove(path.size() - 1);/*回到该节点*/
}
}
static class Solution2 {
public List<List<Integer>> dfs(TreeNode root, int sum) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
// Java 文档中 Stack 类建议使用 Deque 代替 Stack,注意:只使用栈的相关接口
Deque<Integer> path = new ArrayDeque<>();
dfs(root, sum, path, res);
return res;
}
private void dfs(TreeNode node, int sum, Deque<Integer> path, List<List<Integer>> res) {
if (node == null) {
return;
}
if (node.val == sum && node.left == null && node.right == null) {
path.addLast(node.val);
res.add(new ArrayList<>(path));
path.removeLast();
return;
}
path.addLast(node.val);
dfs(node.left, sum - node.val, path, res);
dfs(node.right, sum - node.val, path, res);
path.removeLast();
}
}
/*
方法一:深度优先搜索
*/
static class Solution3 {
List<List<Integer>> ret = new LinkedList<List<Integer>>();
Deque<Integer> path = new LinkedList<Integer>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
dfs(root, targetSum);
return ret;
}
public void dfs(TreeNode root, int targetSum) {
if (root == null) {
return;
}
path.offerLast(root.val);
targetSum -= root.val;
if (root.left == null && root.right == null && targetSum == 0) {
ret.add(new LinkedList<Integer>(path));
}
dfs(root.left, targetSum);
dfs(root.right, targetSum);
path.pollLast();
}
}
/*
方法二:广度优先搜索
*/
static class Solution4 {
List<List<Integer>> ret = new LinkedList<List<Integer>>();
Map<TreeNode, TreeNode> map = new HashMap<TreeNode, TreeNode>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
if (root == null) {
return ret;
}
Queue<TreeNode> queueNode = new LinkedList<TreeNode>();
Queue<Integer> queueSum = new LinkedList<Integer>();
queueNode.offer(root);
queueSum.offer(0);
while (!queueNode.isEmpty()) {
TreeNode node = queueNode.poll();
int rec = queueSum.poll() + node.val;
if (node.left == null && node.right == null) {
if (rec == targetSum) {
getPath(node);
}
} else {
if (node.left != null) {
map.put(node.left, node);
queueNode.offer(node.left);
queueSum.offer(rec);
}
if (node.right != null) {
map.put(node.right, node);
queueNode.offer(node.right);
queueSum.offer(rec);
}
}
}
return ret;
}
public void getPath(TreeNode node) {
List<Integer> temp = new LinkedList<Integer>();
while (node != null) {
temp.add(node.val);
node = map.get(node);
}
Collections.reverse(temp);
ret.add(new LinkedList<Integer>(temp));
}
}
}
LeetCode116 填充每个节点的下一个右侧节点指针
题目详情
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例 1:
输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。
示例 2:
输入:root = []
输出:[]
提示:
树中节点的数量在 [0, 212 - 1] 范围内
-1000 <= node.val <= 1000
进阶:
你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
代码
class LeetCode116 {
public static void main(String[] args) {
Node.printNode(new Solution().connect(new Node(3)));
Node.printNode(new Solution2().connect(new Node(3)));
}
/*
方法一:层次遍历
*/
static class Solution {
public Node connect(Node root) {
if (root == null) {
return root;
}
// 初始化队列同时将第一层节点加入队列中,即根节点
Queue<Node> queue = new LinkedList<Node>();
queue.add(root);
// 外层的 while 循环迭代的是层数
while (!queue.isEmpty()) {
// 记录当前队列大小
int size = queue.size();
// 遍历这一层的所有节点
for (int i = 0; i < size; i++) {
// 从队首取出元素
Node node = queue.poll();
// 连接
if (i < size - 1) {
node.next = queue.peek();
}
// 拓展下一层节点
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
}
// 返回根节点
return root;
}
}
/*
方法二:使用已建立的
*/
static class Solution2 {
public Node connect(Node root) {
if (root == null) {
return root;
}
// 从根节点开始
Node leftmost = root;
while (leftmost.left != null) {
// 遍历这一层节点组织成的链表,为下一层的节点更新 next 指针
Node head = leftmost;
while (head != null) {
// CONNECTION 1
head.left.next = head.right;
// CONNECTION 2
if (head.next != null) {
head.right.next = head.next.left;
}
// 指针向后移动
head = head.next;
}
// 去下一层的最左的节点
leftmost = leftmost.left;
}
return root;
}
}
}
LeetCode230 二叉搜索树中第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 小的值,你将如何优化算法?
代码
class LeetCode230 {
public static void main(String[] args) {
//“掌握解法一,能讲解法二,知道AVL”,够用
System.out.println(new Solution().kthSmallest(TreeNode.buildBinaryTree(new Integer[]{5, 3, 6, 2, 4, null, null, 1}), 3));
System.out.println(new Solution2().kthSmallest(TreeNode.buildBinaryTree(new Integer[]{5, 3, 6, 2, 4, null, null, 1}), 3));
System.out.println(new Solution3().kthSmallest(TreeNode.buildBinaryTree(new Integer[]{5, 3, 6, 2, 4, null, null, 1}), 3));
}
/*
方法一:中序遍历
*/
static class Solution {
public int kthSmallest(TreeNode root, int k) {
Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
--k;
if (k == 0) {
break;
}
root = root.right;
}
return root.val;
}
}
/*
方法二:记录子树的结点数
*/
static class Solution2 {
public int kthSmallest(TreeNode root, int k) {
MyBst bst = new MyBst(root);
return bst.kthSmallest(k);
}
}
static class MyBst {
TreeNode root;
Map<TreeNode, Integer> nodeNum;
public MyBst(TreeNode root) {
this.root = root;
this.nodeNum = new HashMap<TreeNode, Integer>();
countNodeNum(root);
}
// 返回二叉搜索树中第k小的元素
public int kthSmallest(int k) {
TreeNode node = root;
while (node != null) {
int left = getNodeNum(node.left);
if (left < k - 1) {
node = node.right;
k -= left + 1;
} else if (left == k - 1) {
break;
} else {
node = node.left;
}
}
return node.val;
}
// 统计以node为根结点的子树的结点数
private int countNodeNum(TreeNode node) {
if (node == null) {
return 0;
}
nodeNum.put(node, 1 + countNodeNum(node.left) + countNodeNum(node.right));
return nodeNum.get(node);
}
// 获取以node为根结点的子树的结点数
private int getNodeNum(TreeNode node) {
return nodeNum.getOrDefault(node, 0);
}
}
}
网友评论