【LC】二叉树应用强化OJ(2)

1. 二叉搜索树转换成排序双向链表

在线OJ:JZ36 二叉搜索树与双向链表

描述:

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。如下图所示

数据范围:

输入二叉树的节点数 0 \le n \le 10000≤n≤1000,二叉树中每个节点的值 0\le val \le 10000≤val≤1000

要求:

空间复杂度O(1)O(1)(即在原树上操作),时间复杂度 O(n)O(n)

注意:

1.要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继

2.返回链表中的第一个节点的指针

3.函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构

4.你不用输出双向链表,程序会根据你的返回值自动打印输出

输入描述:
二叉树的根节点
返回值描述:
双向链表的其中一个头节点。

💯解题思路:

将二叉搜索树转换为一个升序排列的循环双向链表,我们知道二叉搜索树进行中序遍历得到的就是一个升序排列的序列, 所以我们可以通过中序遍历二叉树, 进而再修改引用指向, 让节点中left引用指向前驱, right指向后继 ;

  1. 使用一个引用prev用来保存中序遍历的前一个结点,使用引用head来记录二叉搜索树的最小结点,即双链表的头结点。

  1. 定义引用cur实现用递归实现中序遍历, 递归的结束条件为 cur == null。

  1. 修改cur节点内left和right引用的指向, cur.left = prev; prev.right = cur;(注意当修改第一个节点的指向的时候, prev是为空的, 所以prev.right = cur操作需要加限制条件prev != null , 也就是说如果prev不为null,说明当前遍历的节点在构造的双链表中存在前驱和后继) ; 然后再更新prev的值为cur。

  1. 最后双链表改造完成后, 我再让head引用向前移动, 直到head指向第一个节点(最小值节点), 返回head。

public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null){
            return null;
        }     
        ConvertChild(pRootOfTree);
        //将指针指向头结点
        TreeNode head = pRootOfTree;
        while(head.left != null){
            head = head.left;
        }
        return head;
    }
    //定义在里面的话 每次递归都会置空
    TreeNode prev = null;
    //换指针指向
    public void ConvertChild(TreeNode cur){
        if(cur == null){
            return;
        }
        ConvertChild(cur.left);
        cur.left = prev;
        if(prev != null){
            prev.right = cur;
        }
        prev = cur; 
        ConvertChild(cur.right);
    }

2. 根据一棵树的前序遍历与中序遍历构造二叉树.

在线OJ:105. 从前序与中序遍历序列构造二叉树

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

提示:

  • 1 <= preorder.length <= 3000

  • inorder.length == preorder.length

  • -3000 <= preorder[i], inorder[i] <= 3000

  • preorder 和 inorder 均 无重复 元素

  • inorder 均出现在 preorder

  • preorder 保证 为二叉树的前序遍历序列

  • inorder 保证 为二叉树的中序遍历序列

💯解题思路:

  1. 根据前序遍历数组元素, 创建根结点。

  1. 根据前序遍历数组找到根结点,通过该根节点取中序遍历数组确定该根节点的左右子树,该根结点左边的中序遍历序列为该结点的左子树,右边的中序遍历序列为该结点的右子树。

  1. 通过分而治之的思想,去创建该树的左子树与右子树。

  1. 假设遍历前序遍历数组的下标为pi,中序遍历数组的起始序列结点下标为ib,结束序列结点下标为ie,所创建树的根结点为ri,则创建一棵树的左子树中序遍历序列下标范围为[ib, ri-1],右子树中序遍历序列下标范围为[ri+1, ie],通过前序遍历的顺序去创建一棵二叉树的根,左子树和右子树。

class Solution {
    //遍历前序,拿到的是根节点
    private int preIndex = 0;
    
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTreeChild(preorder, inorder, 0, inorder.length-1);
    }
    
    private TreeNode buildTreeChild(int[] preorder, int[] inorder, int inbegin, int inend) {
        if(inbegin > inend) {
            return null;
        }
        //创建根节点
        TreeNode root = new TreeNode(preorder[preIndex]); // 1
        
        //找到中序遍历数组中根节点所在位置   // 2
        int rootIndex = findIndex(inorder, inbegin, inend, preorder[preIndex]);
        preIndex++;
        
        //根,左,右 先创建左树, 再创建右树    // 3
        root.left = buildTreeChild(preorder, inorder, inbegin, rootIndex-1);
        root.right = buildTreeChild(preorder, inorder, rootIndex+1, inend);
        return root;
    }
    // 2 
    private int findIndex(int[] inorder, int inbegin, int inend, int val) {
        for (int i = inbegin; i <= inend; i++) {
            if(inorder[i] == val) {
                return i;
            }
        }
        return -1;
    }
}

3. 根据─棵树的中序遍历与后序遍历构造二叉树

在线OJ:从中序与后序遍历序列构造二叉树

给定两个整数数组 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 保证是树的后序遍历

💯解题思路:

这个与上面一个题的是同一类型的题, 只是将前序遍历变为了后续遍历,而我们知道根据后序遍历去获取根节点应该从后向前依次获取,所以该题只需相较于上面的前序遍历改变后序遍历数组遍历顺序和二叉树创建顺序即可。

  1. 根据后序遍历数组元素, 创建根结点。

  1. 根据后序遍历数组从右向左找到根结点,通过该根节点取中序遍历数组确定该根结点的左右子树,该根结点左边的中序遍历序列为该结点的左子树,右边的中序遍历序列为该结点的右子树。

  1. 通过分而治之的思想,去创建该树的右子树与左子树。

  1. 假设 从右向左 遍历后序遍历数组的下标为pi,中序遍历数组的起始序列结点下标为ib,结束序列结点下标为ie,所创建树的根结点为ri,则创建一棵树的左子树中序遍历序列下标范围为[ib, ri-1],右子树中序遍历序列下标范围为[ri+1, ie],通过后序遍历的顺序(从后往前)去创建一棵二叉树的根,右子树和左子树。

class Solution {
    //遍历后序,拿到的是根节点
    private int postIndex = 0;
    
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        postIndex = postorder.length-1;
        return buildTreeChild(postorder, inorder, 0, inorder.length-1);
    }
    
    private TreeNode buildTreeChild(int[] postorder, int[] inorder, int inbegin, int inend) {
        if(inbegin > inend) {
            return null;
        }
        //创建根节点
        TreeNode root = new TreeNode(postorder[postIndex]); // 1
        
        //找到中序遍历数组中根节点所在位置 // 2
        int rootIndex = findIndex(inorder, inbegin, inend, postorder[postIndex]);
        postIndex--;
        
        //左,右,根 先创建右树, 再创建左树 // 3
        root.right = buildTreeChild(postorder, inorder, rootIndex+1, inend);
        root.left = buildTreeChild(postorder, inorder, inbegin, rootIndex-1);
        return root;
    }
    // 2
    private int findIndex(int[] inorder, int inbegin, int inend, int val) {
        for (int i = inbegin; i <= inend; i++) {
            if(inorder[i] == val) {
                return i;
            }
        }
        return -1;
    }
}

4. 二叉树创建字符串

在线OJ:606. 根据二叉树创建字符串

给你二叉树的根节点 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, 104]

  • -1000 <= Node.val <= 1000

💯解题思路:

  1. 按照前序遍历的顺序对二叉树进行遍历,遍历之前需要判断结点左右子树存在情况,根据不同情况添加不同的括号,可以使用StringBuilder对象来对字符串进行拼接。

  1. 首先在StringBuilder对象添加根结点数据,如果根结点的左子树不为空,加左括号,左子树根节点数据,每确定一棵树遍历完了, 就加上右括号; 如果左子树为空,就判断右子树是否存在,如果存在加上一对括号,不存在直接返回。

  1. 遍历完左子树后,再判断右子树是否为空,如果不为空,加左括号,右子树根节点数据,每确定一棵树遍历完了, 就加上右括号, 如果为空直接返回。

class Solution {
    StringBuilder stringBuilder = new StringBuilder();
    public String tree2str(TreeNode root) {
        if(root == null){
            return stringBuilder.toString();
        }
        stringBuilder.append(root.val);
        if(root.left != null){
            stringBuilder.append("(");
            tree2str(root.left);
            stringBuilder.append(")");
        } else {
            if(root.right == null){
                 return stringBuilder.toString();
            }else{
                 stringBuilder.append("()");
            }
        }
        if(root.right != null) {  
            stringBuilder.append("(");
            tree2str(root.right);
            stringBuilder.append(")");
        }
        return stringBuilder.toString();
    }
}

5. 二叉树前序非递归遍历实现

在线OJ:144. 二叉树的前序遍历

💯解题思路:

  1. 使用一个栈储存前序序遍历的结点。

  1. 按照前序遍历的搜索顺序, 获取并保存非空结点的数据, 并将结点入栈。

  1. 遍历到结点为空时,此时将当前遍历的结点更新为栈顶结点的右结点,并将栈顶元素出栈。

  1. 重复上述步骤,直到栈和结点都为空。

class Solution {


    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null){
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                list.add(cur.val);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            cur = top.right;
        }
        return list;
    }

    /*
    public List<Integer> preorderTraversal(TreeNode root) {
        
        List<Integer> ret = new ArrayList<Integer>();

        if(root == null){
            return ret;
        }
        ret.add(root.val);
        List<Integer> leftTree = preorderTraversal(root.left);
        ret.addAll(leftTree);
        List<Integer> rightTree =  preorderTraversal(root.right);
        ret.addAll(rightTree);
        return ret;
    }
    */

6. 二叉树中序非递归遍历实现

在线OJ:94. 二叉树的中序遍历

💯解题思路:

  1. 使用一个栈储存中序遍历的结点。

  1. 按照搜索顺中序遍历的搜索顺序,将非空结点入栈。

  1. 遍历到结点为空时,此时将当前遍历的结点更新为栈顶结点的右结点,获取并保存栈顶结点的数据, 将栈顶元素出栈。

  1. 重复上述步骤,直到栈和结点都为空。

public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null){
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            list.add(top.val); 
            cur = top.right;
        }
        return list;
    }
 

      /*-  
        public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }

    public void inorder(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        inorder(root.left, res);
        res.add(root.val);
        inorder(root.right, res);
    }*/

7. 二叉树后序非递归遍历实现

在线OJ:145. 二叉树的后序遍历

💯解题思路:

  1. 使用一个栈储存后序遍历的结点,定义一个标记引用flag,用来记录上一次遍历完的右子树。

  1. 获取并保存非空结点数据,并将结点入栈。

  1. 遍历到结点为空时,获取栈顶元素,判断栈顶的元素的右子树是否为空和判断该元素的右子树是否与上一次遍历完的右子树flag相等, 如果右子树为空或者该结点的右子树与flag相等, 那么该树已经遍历完成,此时获取并保存根结点数据, 栈顶元素栈并赋值给flag,否则更新当前结点为该结点的右结点去遍历右子树。

  1. 重复上述步骤,直到栈和结点都为空。

class Solution {
    public List<Integer> postorderTraversal(TreeNode root){
        List<Integer> list = new ArrayList<>();
        if(root == null){
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode tmp = null;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            //注意这里的条件  不然会一直死循环
            if(top.right == null || top.right == tmp){
                tmp = stack.pop();
                list.add(top.val); 
            } else {
                cur = top.right;
            }
        }
        return list;
    }


    /*-
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }
    public void inorder(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        inorder(root.left, res);
        
        inorder(root.right, res);
        res.add(root.val);
    }
    */
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏黎世卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值