一文通数据结构与算法之——二叉树+常见题型与解题策略+Leetcode经典题

Posted 尚墨1111

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文通数据结构与算法之——二叉树+常见题型与解题策略+Leetcode经典题相关的知识,希望对你有一定的参考价值。

文章目录

二叉树

1 二叉树基本操作

1.1 二叉树定义

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; };
}

1.2 前、中、后序遍历

前中后的区别是,父节点输出的位置顺序

前:父左右

中:左父右

后:左右父

力扣题库:二叉树深度优先遍历

  • 0144.二叉树的前序遍历
  • 0145.二叉树的后序遍历
  • 0094.二叉树的中序遍历

1.2.1 递归形式

//将二叉树以前序遍历的形式存在列表里,中序后序遍历代码一致,只是add的位置变换
ArrayList<Integer> list = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
    if(root==null){
        return list;
    }else{
        list.add(root.val);
        if(root.left!=null){
            preorderTraversal(root.left);
        }
        if(root.right!=null){
            preorderTraversal(root.right);
        }
    }
    return list; 
}

1.2.2 非递归,迭代形式

迭代法前序遍历,要保持遍历的深度,所以需要用到一个stack来维持

//注意栈是先进后出,所以需要先把右子节点入栈
public static List<Integer> preOrder2(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    Stack<TreeNode2>  stack = new Stack<>();
    if(root == null){
        return list;
    }

    stack.push(root);
    while(!stack.isEmpty()){
        TreeNode node = stack.pop();
        list.add(node.val);

        if(node.right!=null){
            stack.push(node.right);
        }

        if(node.left!= null){
            stack.push(node.left);
        }
    }
    return list;
}

迭代法后续遍历,同前序遍历,只需要把顺序反转一下

public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode2> stack = new Stack<>();

        if(root == null){
            return list;
        }

        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            list.add(node.val);
            //注意这里左右子树的新增顺序也发生了变化
            if(node.left!= null){
                stack.push(node.left);
            }
            if(node.right!=null){
                stack.push(node.right);
            }
        }
        //这句反转的代码参考自leetCode官方,只是反转的操作会使效率降低
        Collections.reverse(list);
        return list;
    }

中序遍历,分析

  • 1.我们必须实现一直深度的入左子节点
  • 2.中间节点在我们入左子节点的时候就相当于已经入栈
  • 3.从左子节点(中间节点)如何跳到右子节点,所以这里需要增加一个临时节点p,用于保存当前的节点
 
 public List<Integer> inorderTraversal(TreeNode2 root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode2> stack = new Stack<>();
        //用临时节点p来保存中间节点信息
        TreeNode2 p = root;
        
        while(!stack.isEmpty() || p!=null){
        	//当前节点如果不为空,那么一直入栈,并且指向自己的左节点,实现中间节点和左子节点的入栈操作
            while(p != null){
                stack.push(p);
                p = p.left;
            }
			//左子节点入栈完毕,开始出站。第一个出栈的就是最底层的那一个左子节点,入list
            p = stack.pop();
            res.add(p.val);
            
            /**
            1. 如果当前节点没有右子节点(叶子结点就没有),那么此时p=null
            2. 如果当前节点不是叶子节点,实现了对右子节点的跳转输出
            3. 所以此时的循环条件是,p非空说明没有遍历完,栈为空则说明出栈完毕,可以结束遍历
            */
            p = p.right;
        }
        return res;
    }

1.3 遍历方式

一来先判空

if(root==null){
    return null;
}

遍历树的两种方式:递归和辅助栈(队列)

递归:典型应用

|-- 判断对称二叉树、相同二叉树、翻转二叉树、二叉树子树、合并二叉树、平衡二叉树

2.相同二叉树(同理)
public boolean isSameTree(TreeNode p, TreeNode q) {
    //双指针,判断
    if(p==null && q==null){
        return true;
    }
    if(p == null || q == null){
        return false;
    }
    return (p.val == q.val) && isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}


4.合并二叉树
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
    if(t1==null || t2 ==null){
        return t1==null?t2:t1;
    }else{
        t1.val = t1.val+t2.val;
        t1.left = mergeTrees(t1.left,t2.left);
        t1.right = mergeTrees(t1.right,t2.right);
        return t1;
    }
}

辅助栈(队列)

//辅助栈
public void test(){
    if(root==null){
        return null;
    }
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.push(root);
    while(!queue.isEmpty()){
        TreeNode node = queue.poll();
        if(node.left!=null){
            queue.push(node.left);
        }
        if(node.right!=null){
            queue.push(node.right);
        }
    }
}

2 剑指 Offer 算法题

2.1 题目列表

递归

剑指 Offer 27. 二叉树的镜像

剑指 Offer 28. 对称的二叉树

剑指 Offer 26. 树的子结构

剑指 Offer 55 - I. 二叉树的深度

剑指 Offer 55 - II. 平衡二叉树

遍历序列化和反序列化

剑指 Offer 32 - I. 从上到下打印二叉树广度优先搜索BFS——队列】

剑指 Offer 32 - II. 从上到下打印二叉树 II

剑指 Offer 32 - III. 从上到下打印二叉树III

剑指 Offer 07. 重建二叉树

剑指 Offer 37. 序列化二叉树

二叉搜索树

剑指Offer 33:二叉搜索树的后序遍历序列

剑指Offer 36:二叉搜索树与双向链表

剑指Offer 54:二叉搜索树的第k个结点

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

剑指 Offer 68 - II. 二叉树的最近公共祖先

剑指Offer 34:二叉树中和为某一值的路径

2.2 实战

剑指 Offer 27. 二叉树的镜像

//将所有左右节点进行互换
//递归
public TreeNode mirrorTree(TreeNode root) {
        if(root==null){
            return null;
        }
        TreeNode left = mirrorTree(root.left);
        TreeNode right = mirrorTree(root.r);
        root.left = right;
        root.right = left;
        return root;
    }

//栈
public TreeNode mirrorTree2(TreeNode root) {
        if(root==null){
            return null;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.push(root);
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            TreeNode temp = node.left;
            node.left = node.right;
            node.right = temp;
            if(node.left!=null){
                queue.push(node.left);
            }
            if(node.right!=null){
                queue.push(node.right);
            }

        }
        return root;
    }

剑指 Offer 28. 对称的二叉树

//用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
//递归法
public boolean isSymmetric(TreeNode root) {
    return root==null || symmetric(root.left,root.right);
}

public boolean symmetric(TreeNode p,TreeNode q){
    if(p==null && q==null) return true;
    if(p==null || q==null || p.val!=q.val) return false;
    return symmetric(p.left,q.right) && symmetric(p.right,q.left);
}

//辅助栈
public boolean symmetric(TreeNode p,TreeNode q) {
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(p);
        queue.offer(q);
        while(!queue.isEmpty()){
            TreeNode leftNode = queue.poll();
            TreeNode rightNode = queue.poll();
//            1.入队列时就已经维持了对照位置,所以可以直接判断相等关系
            if(leftNode==null && rightNode==null) continue;
            if(leftNode==null || rightNode==null ||leftNode.val!=rightNode.val){
                return false;
            }
//            2.入队列
            queue.offer(leftNode.left);
            queue.offer(rightNode.right);

            queue.offer(leftNode.right);
            queue.offer(rightNode.left);
        }
        return true;
    }

剑指 Offer 26. 树的子结构

//输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
//1.递归
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A==null || B==null) return false;
        //这一步很有讲究 这样其实只是判断了三种情况,并没有递归下去。注意递归的精髓
        //return isSame(A,B) || isSame(A.left,B) || isSame(A.right,B);
        //isSame()只是一步判断是否相等的操作
        return isSame(A,B) || isSame(A.left,B) || isSame(A.right,B);

    }
    public boolean isSame(TreeNode p,TreeNode q){
//        2.分情况讨论
//        当B为空,说明已经遍历完了,return true
//        当A为空说明越过了A的叶子节点还没有找到,return false
        if(q==null) return true;
        if(p==null|| p.val!=q.val) return false;
        return isSame(p.left,q.left) && isSame(p.right,q.right);
    }

其他类似的递归操作

        2.相同二叉树(同理)
        public boolean isSameTree(TreeNode p, TreeNode q) {
            //双指针,判断
            if(p==null && q==null){
                return true;
            }
            if(p == null || q == null){
                return false;
            }
            return (p.val == q.val) && isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
        }


        4.合并二叉树
         public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
            if(t1==null || t2 ==null){
                return t1==null?t2:t1;
            }else{
                t1.val = t1.val+t2.val;
                t1.left = mergeTrees(t1.left,t2.left);
                t1.right = mergeTrees(t1.right,t2.right);
                return t1;
            }
        }

        5.平衡二叉树
        理解:左旋:右子树太长了,把它匀到左边。右旋同理。
            1.先将根节点设置为新节点,将原左子树还是设为新的左子树
            2.再把右子树的左子树设为新的右子树
            3.把新节点作为右子树的 left,右子节点作为新的根节点
        双旋转:并不是子树直接左旋右旋就能实现平衡
             左旋时,右子节点的左子树高度 > 右子节点的右子树高度,要先对子树进行右旋

        判断是否为平衡二叉树
        public boolean isBalanced2(TreeNode root) {
            if(root==null){
                return true;
            }else{
                int leftHeight = depth(root.left);
                int rightHeight = depth(root.right);
                if(leftHeight-rightHeight>1 一文通数据结构与算法之——数组+常见题型与解题策略+Leetcode经典题

一文通数据结构与算法之——链表+常见题型与解题策略+Leetcode经典题

一文通数据结构与算法之——回溯算法+常见题型与解题策略+Leetcode经典题

一文通数据结构与算法之——图+常见题型与解题策略+Leetcode经典题

面试知识点之算法数据结构

算法总结二叉树常见算法题目及解题思路汇总