leetcode二叉树

Posted 芋泥*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode二叉树相关的知识,希望对你有一定的参考价值。

目录

1. 二叉树的前序遍历

解题思路

2. 二叉树的中序遍历

 解题思路

3. 二叉树的后序遍历

 解题思路

4. 根据二叉树创建字符串

 解题思路

5. 从前序与中序遍历序列构造二叉树

解题思路

6. 从中序与后序遍历序列构造二叉树

解题思路

7. 二叉树的最近公共祖先

解题思路


1. 二叉树的前序遍历

OJ:二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

输入:root = [1,null,2,3]

输出:[1,2,3]

示例 2:

输入:root = []

输出:[]

示例 3:

输入:root = [1]

输出:[1]

解题思路

1. 如果树为空,直接返回

2. 如果树非空:从根节点位置开始遍历,因为前序遍历规则:根节点、左子树、右子树

        a. 沿着根节点一直往左走,将所经过路径中的节点依次入栈,并访问

        b. 取栈顶元素,该元素取到后,其左子树要么为空,要么已经遍历,可以直接遍历该节点,对于该节点,其左子树已经遍历,该节点也已经遍历,剩余其右子树没有遍历,将其左子树当成一棵新的树开始遍历,继续a步骤。

(前序遍历时每走到一个节点就往ret数组中添加该节点,即第一次访问该节点时添加)

    public List<Integer> preorderTraversal(TreeNode root) 
        List<Integer> ret = new ArrayList<>();
        Stack<TreeNode> s = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !s.empty())
        
            // 每次循环表示要开始访问一颗树了,先将一颗树的左路节点都入栈并访问节点
            // 剩余左路节点的右子树还没访问
            while(cur != null)
            
                ret.add(cur.val);
                s.push(cur);
                cur = cur.left;
            
 
            // 取栈中的节点依次访问节点的右子树
            cur = s.pop();
            cur = cur.right;
        
        return ret;
    

2. 二叉树的中序遍历

OJ:二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

示例 1:

输入:root = [1,null,2,3]

输出:[1,3,2]

示例 2:

输入:root = []

输出:[]

示例 3:

输入:root = [1]

输出:[1]

 解题思路

(1)空树,直接返回

(2)如果树非空:从根节点位置开始遍历,但此时根节点不能遍历,因为中序遍历规则:左子树、根节点、右子树

        a. 沿着根节点一直往左走,将所经过路径中的节点依次入栈,直至当前根节点为空

        b. 取栈顶元素,该元素取到后,其左子树要么为空,要么已经遍历,可以直接遍历该节点,对于该节点,其左子树已经遍历,该节点也已经遍历,剩余其右子树没有遍历,将其左子树当成一棵新的树开始遍历,继续a步骤

(简单来说就是将节点先入栈,出栈时再往ret数组中添加元素。第二次访问时添加该节点)

    public List<Integer> inorderTraversal(TreeNode root) 
        List<Integer> ret = new ArrayList<>();
        // 空树直接返回
        if(null == root)
            return ret;
        
        Stack<TreeNode> s = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !s.empty())
            // 沿这cur一直往左侧走,找到该条路径中最左侧的节点,并保存其所经路径中的所有节点
            while(cur != null)
                s.push(cur);
                cur = cur.left;
            
            // 获取根节点,直接遍历,因为其左侧是空树
            cur = s.peek();
            s.pop();
            ret.add(cur.val);
            // cur的左子树已经遍历,cur已经遍历,剩余其右子树没有遍历,
            // 将其右子树当成一棵新的树进行遍历
            cur = cur.right;
        
        return ret;
    

3. 二叉树的后序遍历

OJ:二叉树的后序遍历

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 

示例 1:

输入:root = [1,null,2,3]

输出:[3,2,1]

示例 2:

输入:root = []

输出:[]

示例 3:

输入:root = [1]

输出:[1]

 解题思路

(1)空树,直接返回

(2)如果树非空:从根节点位置开始遍历,但此时根节点不能遍历,因为后序遍历规则:左子树、右子树、根节点

a. 沿着根节点一直往左走,将所经过路径中的节点依次入栈

b. 取栈顶元素,该元素取到后,其左子树要么为空,要么已经遍历,但是此时该节点不能遍历,除非其右子树不存在或者其右子树已经遍历,才可以遍历该节点, 如果该节点右子树没有遍历,将其右子树作为一棵新的二叉树遍历,继续a步骤

(当第三次访问到节点时,将节点的值添加到lsit数组中)

    public List<Integer> postorderTraversal(TreeNode root) 
        List<Integer> list = new ArrayList<>();
        if(root == null)
            return list;
        
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while(cur != null || !stack.isEmpty())
            while(cur != null)
                stack.push(cur);
                cur = cur.left;
            
            cur = stack.pop();
            if(cur.right == null || cur.right == prev)
                list.add(cur.val);
                prev = cur;
                cur = null;
            else
                stack.push(cur);
                cur = cur.right;
            

        
        return list;
    

4. 根据二叉树创建字符串

OJ:根据二叉树创建字符串

给你二叉树的根节点 root ,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。

空节点使用一对空括号对 "()" 表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。

示例1:

输入:root = [1,2,3,4]

输出:"1(2(4))(3)"

解释:初步转化后得到 "1(2(4)())(3()())" ,但省略所有不必要的空括号对后,字符串应该是"1(2(4))(3)" 。

示例 2:

输入:root = [1,2,3,null,4]

输出:"1(2()(4))(3)"

解释:和第一个示例类似,但是无法省略第一个空括号对,否则会破坏输入与输出一一映射的关系。

 解题思路

1. 如果树空,转化结束

2. 如果树非空

        a. 先转化根节点

        b. 转化根节点的左子树

如果根的左子树非空或者左子树空但是右子树非空:( 递归转化左子树 ), 注意将转化结果内嵌到()中

        c. 转化根节点的右子树

 如果根的右子树非空:( 递归转化右子树 ), 注意将转化结果内嵌到()中

class Solution 
String str;
    public String tree2str(TreeNode t) 
        StringBuilder sb = new StringBuilder();
        tree2str(t, sb);
        return sb.toString();
    
    public void tree2str(TreeNode t, StringBuilder str) 
        if(null == t)
            return;
        
        // 先将根节点的数据放到str中
        str.append(t.val);
        // 处理根节点的左子树
        if(null != t.left || null != t.right)
        
            // 左子树非空,递归转化左子树
            str.append("(");
            tree2str(t.left, str);
            str.append(")");
        
        // 再检测t的右子树,如果右子树为空,不增加任何内容
        if(null != t.right)
            // 递归处理右子树
            str.append("(");
            tree2str(t.right, str);
            str.append(")");
        
    

5. 从前序与中序遍历序列构造二叉树

OJ:从前序与中序遍历序列构造二叉树

给定两个整数数组 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]

解题思路

从前序遍历中获取根节点,中序遍历中根节点前面的是左子树的部分,中序遍历中根节点后面的是右子树部分。使用index下标来遍历前序数组。

1. 从前序遍历结果中获取到树的根节点

2. 在中序遍历结果中确定根节点的位置,按照该位置将中序遍历结果分为两部分

        左半部分是根节点的左子树,递归创建根节点的左子树

        右半部分是根节点的右子树,递归创建根节点的右子树

class Solution 
    public TreeNode buildTree(int[] preorder, int[] inorder) 
        return buildTreeHelper(preorder,inorder,0,inorder.length - 1);
    
    int index = 0;
    private TreeNode buildTreeHelper(int[] preorder, int[] inorder, int left, int right) 
        if(index == inorder.length )
            return null;
        
        if (left > right)
            return null;
        
        int pos = find(inorder,preorder);
        TreeNode root = new TreeNode(preorder[index]);
        index ++;
        root.left = buildTreeHelper(preorder,inorder,left,pos - 1);
        root.right = buildTreeHelper(preorder,inorder,pos + 1,right);
        return root;
    

    private int find(int[] inorder,int[] preorder) 
        for (int i = 0; i < inorder.length; i++) 
            if (inorder[i] == preorder[index])
                return i;
            
        
        return -1;
    

6. 从中序与后序遍历序列构造二叉树

给定两个整数数组 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]

解题思路

先看看同一颗树的前序遍历结果preorder = [3,9,20,15,7]和后序遍历的结果postorder = [9,15,7,20,3],对比一下这两。发现将后序遍历的结果反转->[3,20,7,15,9],前序遍历是根左右,后序遍历反转后的是根右左所以我们只需要将后序遍历的结果反转一下,然后向上一题那样构建二叉树,只不过是先构建右子树再构建左子树

class Solution 
    public TreeNode buildTree(int[] inorder, int[] postorder) 
        reverse(postorder);
        return buildTreeHelper(postorder,inorder,0,inorder.length - 1);
    
    private void reverse(int[] postorder)
        for(int i = 0;i < postorder.length/2;i ++)
            int temp = postorder[i];
            postorder[i] = postorder[postorder.length - i - 1];
            postorder[postorder.length - i - 1] = temp;
        
    
    
    int index = 0;
    private TreeNode buildTreeHelper(int[] preorder, int[] inorder, int left, int right) 
        if(index == preorder.length )
            return null;
        
        if (left > right)
            return null;
        
        int val = preorder[index];
        index ++;
        int pos = find(inorder,val);
        TreeNode root = new TreeNode(val);
        
        root.right = buildTreeHelper(preorder,inorder,pos + 1,right);
        root.left = buildTreeHelper(preorder,inorder,left,pos - 1);
        return root;
    

    private int find(int[] inorder,int val) 
        for (int i = 0; i < inorder.length; i++) 
            if (inorder[i] == val)
                return i;
            
        
        return -1;
    

7. 二叉树的最近公共祖先

OJ:二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1

输出:3

解释:节点5和节点1的最近公共祖先是节点3

示例 2:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4

输出:5

解释:节点5和节点1的最近公共祖先是节点3

解题思路

这题不太熟,暂时不写思路。

class Solution 
    TreeNode lca;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) 
        find(root,p,q);
        return lca;
    

    private boolean find(TreeNode root, TreeNode p, TreeNode q)
        if(root == null)
            return false;
        

        int left = find(root.left,p,q) ? 1 : 0;
        int right = find(root.right,p,q) ? 1 : 0;
        int mid = (root == p || root == q) ? 1 : 0;
        if(left + right + mid == 2)
            lca = root;
        
        return left + right + mid > 0;
    

leetcode刷穿二叉树

拿下二叉树的第五天


专辑完结,欢迎查看本系列文章:
leetcode刷穿二叉树(一)
leetcode刷穿二叉树(二)
leetcode刷穿二叉树(三)
leetcode刷穿二叉树(四)
leetcode刷穿二叉树(五)
leetcode刷穿二叉树(六)
建议收藏~

这里有leetcode题集分类整理!!!

  • leetcode 501. 二叉搜索树中的众数
  • leetcode 236. 二叉树的最近公共祖先
  • leetcode 35. 二叉搜索树的最近公共祖先
  • leetcode 701. 二叉搜索树中的插入操作
  • leetcode 450. 删除二叉搜索树中的节点

1、二叉搜索树中的众数
题目难度:简单
题目描述:

解题思路:

根据题意,我们得知,必须要遍历完所有节点才能知道答案,并且属于统计类对问题,因此采取什么遍历方式都可以达到目的,这里我们利用BST中序遍历的有序性,可以省去一个哈希表来存取元素的数量。
采用变量如下:

  • count 变量来统计当前元素个数;
  • maxCount 统计当前众数的元素对应的个数;
  • base 记录上次遍历到的数字(以拿来后续的判断)

采用方法如下:

  • inOrderTraversal(TreeNode node) 中序遍历BST
  • update(int x) 用于每次遍历时判断当前节点对值是否属于众数
class Solution 
    private List<Integer> res = new ArrayList<>();
    private int base, count, maxCount;
    public int[] findMode(TreeNode root) 
        inOrderTraversal(root);
        int len = res.size();
        int[] ans = new int[len];
        for (int i = 0; i < len; i ++) 
            ans[i] = res.get(i);
        
        return ans;
    

    public void inOrderTraversal(TreeNode node) 
        if (node == null) return;
        inOrderTraversal(node.left);
        update(node.val);
        inOrderTraversal(node.right);
    

    public void update(int x) 
        if (x == base) 
            count ++;
         else 
            base = x;
            count = 1;
        
        if (count == maxCount) 
            res.add(x);
        
        if (count > maxCount) 
            maxCount = count;
            res.clear();
            res.add(x);
        
    

2、二叉树的最近公共祖先
题目难度:中等
题目描述:


解题思路一:

求公共祖先类问题,需要自底向上遍历树,而自底向上就可以采用后序遍历的性质来实现。
递归的终止条件:

  • 叶子节点不是p、q,返回null
  • 节点是p或q, 返回这个节点

后序遍历的处理逻辑:

  • 同时不为空,说明是p、q为该节点的左右孩子辈,返回该节点
  • 一个为空,说明找到其中一个节点,或找到了公共祖先,返回不为空的节点。
  • 都为空,则没有找到左右节点或公共祖先,返回null

需要注意的一点:自底向上搜索的时候需要遍历整棵树,无法提前返回

class Solution 
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) 
        if (root == null || root.val == p.val ||root.val == q.val) return root;
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        if (left != null && right != null) return root;
        else if (left == null && right != null) return right;
        else if (left != null && right == null) return left;
        else return null;
    

解题思路二:

采用将父节点存入哈希表的方式
执行方法:

  • 在dfs的过程中将节点的值,对应的父节点存入哈希表(parent)中,用来后续求得某个节点的祖先节点集合(visited中)。
  • 遍历某个目标节点(p或q),将它的祖先节点们加入visited集合之中。
  • 遍历另一个目标节点,从哈希表中不断的向上获取父亲节点,直到在visited集合中找到这个父节点,那么这个父节点就是最小公共祖先了。
class Solution 
    HashMap<Integer, TreeNode> parent = new HashMap<>();
    Set<Integer> visited = new HashSet<>();

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) 
        dfs(root);
        while (p != null) 
            visited.add(p.val);
            p = parent.get(p.val);
        

        while (q != null) 
            if (visited.contains(q.val)) 
                return q;
            
            q = parent.get(q.val);
        
        return null;
    

    public void dfs(TreeNode node) 
        if (node.left != null) 
            parent.put(node.left.val, node);
            dfs(node.left);
        
        if (node.right != null) 
            parent.put(node.right.val, node);
            dfs(node.right);
        
    

3、 二叉搜索树的最近公共祖先
题目难度:简单
题目描述:

解题思路:

这道题比第二题简单的一点就是在BST中寻找公共祖先。
可以利用BST查找遍历的特性:左小右大 来很方便的去获得我们想要的信息
在这道题中: 找公共祖先之需要满足
lca < [p, q] 就向右查找
[p,q] < lca 就向左查找
直到 p < lca (lowest common ancestor) < q 即可返回

class Solution 
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) 
        if (root.val > p.val && root.val > q.val) 
            return lowestCommonAncestor(root.left, p, q);
         else if (root.val < p.val && root.val < q.val) 
            return lowestCommonAncestor(root.right, p, q);
         else return root;
    

4、二叉搜索树中的插入操作
题目难度:中等
题目描述:

解题思路:

插入一个新的值二叉搜索树:

  • 递归遍历BST:
    • 如果目标数值比当前节点小,向左遍历
    • 如果目标数值比当前节点大,向右遍历
    • 一直递归到空节点,即为待插入的位置
  • 找到插入的位置后新建节点,返回
class Solution 
    public TreeNode insertIntoBST(TreeNode root, int val) 
        if (root == null) 
            TreeNode node = new TreeNode(val);
            return node;
        
        if (root.val > val) root.left = insertIntoBST(root.left, val);
        if (root.val < val) root.right = insertIntoBST(root.right, val);
        return root;
    

5、删除二叉搜索树中的节点
题目难度:中等
题目描述:

解题思路:

删除搜索二叉搜索树的节点
首先确定递归的终止条件:
有以下几种情况:

  • 节点为空 -> 没有搜索到目标节点,返回空
  • 找到节点,节点没有左右孩子 -> 直接将节点置空。
  • 找到节点,节点只有左孩子 -> 返回左孩子
  • 找到节点,节点只有右孩子 -> 返回右孩子
  • 找到节点,有两个孩子的时候,我们有两种可选择对方案:(结果性质一样)
    • 将左孩子插入到右孩子的最左端,将右孩子返回
    • 将右孩子插入到左孩子的最右端,将左孩子返回
class Solution 
    public TreeNode deleteNode(TreeNode root, int key) 
        if (root == null) return null;
        if (root.val == key && root.left == null && root.right == null) 
            return null;
        
        if (root.val == key && root.left != null && root.right == null) 
            return root.left;
        
        if (root.val == key && root.left == null && root.right != null) 
            return root.right;
        
        if (root.val == key && root.left != null && root.right != null) 
            TreeNode l = root.left;
            TreeNode pl = root;
            while (l != null) 
                pl = l;
                l = l.right;
            
            pl.right = root.right;
            return root.left;
        
        root.left = deleteNode(root.left, key);
        root.right = deleteNode(root.right, key);
        return root;
    

专辑完结,欢迎查看本系列文章:
leetcode刷穿二叉树(一)
leetcode刷穿二叉树(二)
leetcode刷穿二叉树(三)
leetcode刷穿二叉树(四)
leetcode刷穿二叉树(五)
leetcode刷穿二叉树(六)
原创不易,三连支持一下喔~

以上是关于leetcode二叉树的主要内容,如果未能解决你的问题,请参考以下文章

数据结构二叉树相关面试题 Java版 LeetCode题 ------- 二叉树

(Java)构造二叉树OJ题(LeetCode105 根据前序与中序构造二叉树,LeetCode106 根据后序与中序构造二叉树)

LeetCode Java刷题笔记—226. 翻转二叉树

LeetCode Java刷题笔记—110. 平衡二叉树

leetcode刷题27.二叉树的直径——Java版

LeetCode Java刷题笔记—101. 对称二叉树