1 二叉树的右视图
示例 1:
输入: [1,2,3,null,5,null,4] 输出: [1,3,4]
法一:
思路:根 右 左遍历二叉树
—> 保证每层最先访问到都是最右边的节点
// 二叉树的右视图 法一:根左右 遍历二叉树 每个深度第一个出现的即为右视图看到的
static List<Integer> result = new ArrayList<>();
public static List<Integer> rightView(TreeNode root) {
postOrder(root, 0);
return result;
}
public static void postOrder(TreeNode root, int depth) {
if (root == null) {
return;
}
if (depth == result.size()) {
result.add(root.val);
}
depth++;
postOrder(root.right, depth);
postOrder(root.left, depth);
}
法二:
思想:层序遍历
记录每一层的最后一个元素
class Solution {
public List<Integer> rightSideView(TreeNode root) {
// 创建列表 用于返回最终结果
List<Integer> result = new ArrayList<>();
// 判空
if(root == null){
return result;
}
// 创建队列用于层序遍历
Queue<TreeNode> queue = new LinkedList<>();
// 入队
queue.offer(root);
// 当队列不为空时 层序遍历二叉树
while(!queue.isEmpty()){
int currentSize = queue.size();
for(int i = 0; i < currentSize; i++){
// 出队
TreeNode node = queue.poll();
// 左右节点不为空时 入队
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
// 若为当前层最后一个元素 将其值添加到列表中
if(i == currentSize-1){
result.add(node.val);
}
}
}
return result;
}
2 二叉树展开为链表
给你二叉树的根结点 root
,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode
,其中right
子指针指向链表中下一个结点,而左子指针始终为null
。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。
示例 1:
输入:root = [1,2,5,3,4,null,6] 输出:[1,null,2,null,3,null,4,null,5,null,6]
思想:
- 先序遍历将结果存入列表中
- 然后在将列表中每一个节点的左子树设置为null ,右子树设置为下一个节点
class Solution {
public void flatten(TreeNode root) {
List<TreeNode> list = new ArrayList<TreeNode>();
preOrder(root,list);
int size = list.size();
// 一定要从1开始遍历 0索引无前驱
for(int i = 1; i < size; i++){
// 得到当前节点 和当前节点的前一个和后一个节
TreeNode prev = list.get(i-1);
TreeNode curr = list.get(i);
//将上一个节点的左子树设为 null
prev.left = null;
// 将上一个节点的右子树设为当前节点
prev.right = curr;
}
}
// 二叉树的先序遍历 将遍历到的结果添加到列表中
public void preOrder(TreeNode root, List<TreeNode> list){
if(root != null){
list.add(root);
preOrder(root.left, list);
preOrder(root.right, list);
}
}
}
3 从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1] 输出: [-1]
方法一
思想:递归
注意点:每次递归构造二叉树的上界和下界
步骤:
- 构造递归函数bulidMyTree
先判断是否为空的二叉树 若左边界大于右边界 则直接返回null
- 找根节点对应的索引(先序遍历中第一个即为根节点),根据先序找到中序遍历中根节点的位置
- 创建根节点
- 求左子树节点的数目 (用于定位先序遍历中左右子树的边界)
- 递归调用函数 构造根节点的左右子树
- 主函数
- 获取数组的长度(先序 中序数组中任意一个)
- 遍历数组,将中序遍历 值-索引 映射到哈希表 key-value 中
- 调用递归函数构造二叉树
class Solution {
Map<Integer,Integer> indexMap = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
// 构造中序遍历的哈希映射
for(int i=0; i < n; i++){
indexMap.put(inorder[i], i);
}
return buildMyTree(preorder, inorder, 0, n-1, 0, n-1);
}
// 递归函数 递归的构造二叉树
public TreeNode buildMyTree(int[] preorder, int[] inorder, int pre_left, int pre_right, int in_left, int in_right) {
// 若左边界大于右边界 则二叉树为空 直接返回null
if(pre_left > pre_right){
return null;
}
// 找根节点 在先序 和中序中的索引(下标值)
int pre_root = pre_left;
int in_root = indexMap.get(preorder[pre_root]);
// 建立根节点
TreeNode root = new TreeNode(preorder[pre_root]);
// 计算左子树 右子树节点 个数
int left_treeSize = in_root - in_left;
int right_treeSize = in_right - in_root;
// 递归构造根节点的左子树和右子树 注意构造二叉树时 左右 节点的边界值
// 先序遍历中「从 左边界+1 开始到 pre_left + left_treeSize」个元素
// 对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = buildMyTree(preorder, inorder,
pre_left + 1, pre_left + left_treeSize,
in_left, in_root-1);
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素对应了
// 中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = buildMyTree(preorder, inorder,
pre_left + left_treeSize + 1, pre_right,
in_root + 1, in_right);
// 返回递归构建的二叉树
return root;
}
}
方法二
思想:借助栈
- 用一个栈和一个指针辅助进行二叉树的构造。初始时栈中存放了根节点,指针指向中序遍历中的第一个节点
- 依次遍历前序遍历数组(从1开始) 如果index等于栈顶节点 不断的弹出栈顶节点并向右移动指针,将当前节点作为最后一个弹出的节点的右儿子
- 如果不同,把档期那节点当做栈顶节点的左儿子
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
// 若数组为空 证明二叉树为null
if(preorder == null || preorder.length == 0){
return null;
}
// 创建根节点 为先序遍历的第一个节点
TreeNode root = new TreeNode(preorder[0]);
// 借助栈 构造二叉树
Deque<TreeNode> stack = new LinkedList<>();
// 根节点入队
stack.push(root);
int inorderIndex = 0; // 初始化指针指向中序遍历的第一个值 索引
for(int i = 1; i < preorder.length; i++){
int preorderVal = preorder[i];
// 获取栈顶元素
TreeNode node = stack.peek();
// 如果当前栈顶节点的值不等于中序遍历中的值
if(node.val != inorder[inorderIndex]){
// 创建左子节点 并入栈
node.left = new TreeNode(preorderVal);
stack.push(node.left);
}else{
// 当栈不为空会这栈顶值与当前中序遍历的值相等时
while(! stack.isEmpty() && stack.peek().val == inorder[inorderIndex]){
node = stack.pop();
inorderIndex++;
}
// 右节点入栈
node.right = new TreeNode(preorderVal);
stack.push(node.right);
}
}
return root;
}
}