二叉树第一期

刷题先刷二叉树,因为二叉树是后来其他高阶算法的基础,也是理解递归的最佳途径。

东哥这里有几句话非常经典:

  • 写递归算法的关键是要明确函数的「定义」是什么,然后相信这个定义,利用这个定义推导最终结果,绝不要跳入递归的细节。
  • 写树相关的算法,简单说就是,先搞清楚当前 root 节点「该做什么」以及「什么时候做」,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情。
  • 所谓「该做什么」就是让你想清楚写什么代码能够实现题目想要的效果,所谓「什么时候做」,就是让你思考这段代码到底应该写在前序、中序还是后序遍历的代码位置上。

226. 翻转二叉树

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return root;
        //函数定义:给定一棵树的根节点,就会将这棵树翻转成功
        //思路很简单,把左子树翻转,再把右子树翻转,这些都通过函数定义完成
        //root节点要做的:把左子节点和右子节点交换
        
        //翻转左子树
        invertTree(root.left);
        //翻转右子树
        invertTree(root.right);
        //root节点要做的
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        return root;
    }
}

114. 二叉树展开为链表

/**
 * 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;
 *     }
 * }
 */
// 定义:将以 root 为根的树拉平为链表
void flatten(TreeNode root) {
        //在做二叉树的题的时候,反正他肯定要递归左子树和右子树的,而root的操作就只有前序中序和后序三种选择,
        //不妨想想这root操作选选择哪一种才能解开这道题,三种选择都考虑一下!

        //函数定义:给定一棵树的根节点,flatten会将其展开为一个单链表
        //遵循函数定义,将左子树和右子树分别展开为单链表
        //root节点要做的:把右子树变成左子树,再把右子树接在左子树的下面
    // base case
    if (root == null) return;
    
    flatten(root.left);
    flatten(root.right);

    /**** 后序遍历位置 ****/
    // 1、左右子树已经被拉平成一条链表
    TreeNode left = root.left;
    TreeNode right = root.right;
    
    // 2、将左子树作为右子树
    root.left = null;
    root.right = left;

    // 3、将原先的右子树接到当前右子树的末端
    TreeNode p = root;// 注意这个地方一定是TreeNode p = root, 不能是 TreeNode p = root.right,因为递归到叶子结点的时候, root.right是空的,这个时候p就是null,再p.right就相当于null.right会空指针异常
    while (p.right != null) {
        p = p.right;
    }
    p.right = right;
}

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

        //函数定义:给定一棵树的root节点,将同一层所有节点连起来
        //最简单的思路,root操作是连接两个子节点,然后递归,connet(root.left, root.right)
        //但是这样会出错,因为一个root节点只能将自己的孩子节点连接,而不能连接不同父节点的两个相邻节点
        //因此,这道题的函数定义数不能满足要求的,我们至少需要两个相邻节点来完整连接
// 主函数
Node connect(Node root) {
    if (root == null) return null;
    connectTwoNode(root.left, root.right);
    return root;
}

// 辅助函数
void connectTwoNode(Node node1, Node node2) {
    if (node1 == null || node2 == null) {
        return;
    }
    /**** 前序遍历位置 ****/
    // 将传入的两个节点连接
    node1.next = node2;
    
    // 连接相同父节点的两个子节点
    connectTwoNode(node1.left, node1.right);
    connectTwoNode(node2.left, node2.right);
    // 连接跨越父节点的两个子节点
    connectTwoNode(node1.right, node2.left);
}

posted @ 2022-01-02 18:57  十一又十一  阅读(502)  评论(0)    收藏  举报