【Data structure & Algorithm】做题笔记-二叉树

本文详细探讨了二叉树的各种遍历方法,包括前序、中序、后序以及层级遍历,并提供了相关算法题目的解题思路。通过递归思想解析了如何构造、翻转和连接二叉树节点,以及如何根据遍历序列还原二叉树。此外,还介绍了完全二叉树的节点计算优化方法,总结了二叉树遍历的代码框架和特点。
摘要由CSDN通过智能技术生成

方法论

二叉树的遍历方式是递归,写递归函数的关键是要明确函数的「定义」是什么,然后相信这个定义,利用这个定义推导最终结果,绝不要跳入递归的细节。换句话说,先搞清楚当前 root 节点该做什么,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情。把题目的要求细化成每个节点(或每几个节点)需要做的事情。

如,计算一棵二叉树共有几个节点:

// 定义:count(root) 返回以 root 为根的树有多少节点
int count(TreeNode root) {
   
    // base case
    if (root == null) return 0;
    // 自己加上子树的节点数就是整棵树的节点数
    return 1 + count(root.left) + count(root.right);
}

root 本身就是一个节点,加上左右子树的节点数就是以 root 为根的树的节点总数。左右子树的节点数怎么算?其实就是计算根为 root.left 和 root.right 两棵树的节点数,按照定义,递归调用 count 函数即可算出来。

框架:

/* 二叉树遍历框架 */
void traverse(TreeNode root) {
   
    // 前序遍历位置,在此处访问root.val
    traverse(root.left)
    // 中序遍历位置,在此处访问root.val
    traverse(root.right)
    // 后序遍历位置,在此处访问root.val
}

226-翻转二叉树

题目:输入一个二叉树根节点 root,把整棵树镜像翻转。
Invert a binary tree

思路:只要把二叉树上的每一个节点的左右子节点进行交换,最后的结果就是完全翻转之后的二叉树,如图所示。
在这里插入图片描述

// 将整棵树的节点翻转
TreeNode invertTree(TreeNode root) {
   
    // 结束递归的条件
    if (root == null) {
   
        return null;
    }

    /**** 前序遍历位置 ****/
    // root 节点需要交换它的左右子节点
    TreeNode tmp = root.left;
    root.left = root.right;
    root.right = tmp;

    // 让左右子节点继续翻转它们的子节点
    invertTree(root.left);
    invertTree(root.right);

    return root;//注意return的是根节点
}

116-填充每个节点的下一个右侧节点指针

题目
在这里插入图片描述
难点在于如何连接不同子树上的节点,如节点5、6。如果递归函数的参数只有1个(传入1个节点),节点5、6是无法被连接的,因为每次递归,递归函数的作用域(所能访问到的节点)为当前节点、当前节点的左节点、当前节点的右节点,形成一个三角形作用域,节点5、6处于不同作用域中(即处于不同的三角形中),无法同时被访问到,故无法进行连接。

那我们就把递归函数的参数设置为2个(传入2个节点),每次递归,递归函数的作用域(所能访问到的节点)形成了一个梯形,包含6个节点,即参数节点1、参数节点1的左节点、参数节点1的右节点、参数节点2、参数节点2的左节点、参数节点2的右节点。这样节点5、6就可以被同时访问到了,可以进行连接了。

代码如下,示意图如下。

//错误代码,递归函数的参数只有1个
Node connect(Node root) {
   
    if (root == null || root.left == null) {
   
        return root;
    }

    root.left.next = root.right;

    connect(root.left);
    connect(root.right);

    return root;
}
//正确代码,递归函数的参数有2个
//主函数
    public Node connect(Node root) {
   
        if(root == null){
   
            return null;
        }
        connect2Nodes(root.left,root.right);
        return root;
    }
//辅助函数
    public void connect2Nodes(Node n1,Node n2){
   
        //n1在左,n2在右(不要反了)
        //结束递归的条件,注意:这是一棵完美二叉树
        if(n1 == null || n2 == null){
   //由于是完美二叉树,n1、n2实际上会同时为null,或同时不为null
            return;
        }
        /**** 前序遍历位置 ****/
        // 将传入的两个节点连接
        n1.next = n2;
        // 连接相同父节点的两个子节点
        connect2Nodes(n1.left,n1.right);
        connect2Nodes(n2.left,n2.right);
        // 连接跨越父节点的两个子节点
        connect2Nodes(n1.right,n2.left);
    }

在这里插入图片描述

114-将二叉树展开为链表

题目
关键点是要“脑补”出二叉树被拉平(flatten)的过程,过程如下图所示,蓝色框表示当前步骤所关注的二叉树部分,先关注root的左子树,将其拉平,然后关注root的右子树,将其拉平(本来就是平的),然后关注整体,将整体拉平。“拉平”具体又是怎么实现呢?以root的左子树为例,将根节点的左节点插入到根节点与右节点之间;以整体为例,将根节点(flatten后)的左子树,插入到根节点与(flatten后)的右子树之间。所谓的“拉平”就是我们要写的递归方法。
在这里插入图片描述

    public void flatten(TreeNode root) {
   
        if(root == null){
   
            return;
        }

        //先拉平左子树
        flatten(root.left);
        //再拉平右子树
        flatten(root.right);

        /**** 后序遍历位置 ****/
        //将拉平后的左子树插入到根节点与右子树之间
        TreeNode rootLeft = root.left;
        TreeNode rootRight = root.right;

        if(rootLeft != null){
   
            root.left = null;
            root.right = rootLeft;
            //将右子树拼接到原左子树的最右下末端(原左子树已经被flatten过了,一定是\形状)
            TreeNode connectNode = rootLeft;
            while(connectNode.right != null){
   
                connectNode = connectNode.right;
            }
            //connectNode为原左子树的最右下末端节点
            connectNode.right = rootRight;
        }
        
    }

654-构造最大二叉树

题目
思路:先要找到根节点root,即数组中的最大值,然后将数组中最大值左边的数构建成最大二叉树,作为根节点的左子树,然后将数组中最大值右边的数构建成最大二叉树,作为根节点的右子树。

    public TreeNode constructMaximumBinaryTree(int[] nums) {
   
        return build(nums,0,nums.length-1);
    }

    public TreeNode build(int[] nums,int start,int end){
   //start,end表明使用数组哪一步分
        if(end < start){
   
            return null;
        }

        //找数组中的最大值及其索引,作为根节点
        int max = nums[start];
        int index = start;
        for(int i=start+1;i<=end;i++){
   
            if(nums[i] > max){
   
                max = nums[i];
                index = i;
            }
        }
        TreeNode root = new TreeNode(max);

        //将最大值左边部分构建成最大二叉树,作为根节点的左子树
        root.left = build(nums,start,index-1);
        //将最大值右边部分构建成最大二叉树,作为根节点的右子树
        root.right = build(nums,index+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值