美文网首页
leetcode科学刷题(1)-二叉树(数据结构篇)

leetcode科学刷题(1)-二叉树(数据结构篇)

作者: 1994_老叶 | 来源:发表于2020-12-23 22:55 被阅读0次

    网上冲浪的时候,偶然看到科学刷leetcode算法题的方法--算法模版,实践是检验真理的唯一标准,我决定按照他的方法试一试,他这个上面是go语言实现的,我用Java跑一遍试试。

    先简单写一下,大致列一下,周末时间来完成这个,完成后删除这句话

    二叉树

    1.我的二叉树的代码:

    二叉树节点

    package data.structure.binarytree.model;
    
    
    /**
     * 二叉树的节点数据结构
     */
    public class TreeNode {
    
        //值
        private  int value;
    
        //左孩子
        private TreeNode leftChild;
    
        //右孩子
        private TreeNode rightChild;
    
        public int getValue() {
            return value;
        }
    
        public void setValue(int value) {
            this.value = value;
        }
    
        public TreeNode getLeftChild() {
            return leftChild;
        }
    
        public void setLeftChild(TreeNode leftChild) {
            this.leftChild = leftChild;
        }
    
        public TreeNode getRightChild() {
            return rightChild;
        }
    
        public void setRightChild(TreeNode rightChild) {
            this.rightChild = rightChild;
        }
    
        public TreeNode(int value) {
            this.value = value;
        }
    }
    
    

    二叉树:

    package data.structure.binarytree.model;
    
    import java.util.LinkedList;
    import java.util.Queue;
    
    public class BinaryTree {
    
       //根结点
       private TreeNode root=null;
    
       public BinaryTree(){}
    
       public BinaryTree(TreeNode root) {
           this.root = root;
       }
    
       /**
        *  清除子树
        */
       public void clear(TreeNode node){
           if (node !=null){
               clear(node.getLeftChild());
               clear(node.getRightChild());
               node = null;//清除节点
           }
       }
    
       /**
        *  清除根结点
        */
       public void clear(){
           clear(root);
       }
    
       /**
        * 判断是否为空
        */
       public boolean isEmpty(){
           return root == null;
       }
    
       /**
        * (递归)获取以某节点的树高(DFS)
        * @param node
        * @return
        */
       public int hightRecursion(TreeNode node){
    
           if (node == null){
               return 0;//当前节点为null则树高为0
           }else{
               int l = hightRecursion(node.getLeftChild());//递归获取左子树的树高
               int r = hightRecursion(node.getRightChild());//递归获取右子树的树高
               return l > r? (l+1) : (r+1);//左右子树高的为树高+自身这一层
           }
    
       }
    
       /**
        * (非递归)获取以某节点的树高(BFS)
        * @param node
        * @return
        */
       public int hight(TreeNode node){
    
           if (node == null){
               return 0;//当前节点为null则树高为0
           }
           int treeHight = 0;
    
           Queue<TreeNode> tempNode = new LinkedList<>();
           tempNode.offer(node);
           while (!tempNode.isEmpty()){
               int count = tempNode.size();
               while (count > 0){
                   node = tempNode.poll();
                   if (node.getLeftChild() != null){
                       tempNode.offer(node.getLeftChild());
                   }
                   if (node.getRightChild() != null){
                       tempNode.offer(node.getRightChild());
                   }
                   count--;
               }
               treeHight++;
    
           }
    
           return treeHight;
    
       }
    
       /**
        * 获取树高
        * @return
        */
       public int hight(){
           return hight(root);
       }
    
    
       /**
        * 获取某子数的节点数
        * @param node
        * @return
        */
       public int size(TreeNode node){
           if (node == null){
               return 0;//当前节点为null则节点数为0
           }else{
               //当前节点+左子树节点数+右子树节点数
               return 1 + size(node.getLeftChild())+size(node.getRightChild());
           }
       }
    
       /**
        * 当前树的节点数
        * @return
        */
       public int size(){
           return size(root);
       }
    
       /**
        * 寻找某节点在某子树中的父亲节点(还可以在node中增加parent字段来解决)
        * @param subTree
        * @param node
        * @return
        */
       public TreeNode getParent(TreeNode subTree, TreeNode node){
           if (subTree == null){
               return null;//子树为null
           }
    
           if (subTree.getLeftChild() == node || subTree.getRightChild() == node){
               return subTree;
           }
           TreeNode parent;
           parent = getParent(subTree.getLeftChild(),node);//从左子树中找寻
    
           if (parent != null){
               return  parent;
           }
           parent = getParent(subTree.getRightChild(), node);//从右子树找寻
    
           return parent;
       }
    
       /**
        * 从树中找节点的父节点
        * @param node
        * @return
        */
       public TreeNode getParent(TreeNode node){
           return getParent(root,node);
       }
    
       /**
        * 是否是平衡二叉树
        * @param node
        * @return
        */
       public  boolean isBalanced(TreeNode node){
           
           int hight = higthForBalance(root);
           if ( hight >= 0){
               return true;
           }
           return false;
    
       }
    
       /**
        * 自底向上判断,如果存在而左右子树树高相差大于1的则是不平衡,最后输出-1;如果是平衡的,则是正常树高>=0
        * @param node
        * @return
        */
       private int higthForBalance(TreeNode node){
           if (node == null){
               return 0;
           }
    
           int leftHight = higthForBalance(node.getLeftChild());
           int rightHight = higthForBalance(node.getRightChild());
    
           if (leftHight == -1 || rightHight == -1 || Math.abs(leftHight-rightHight) >1){
               return -1;
           }else {
               return leftHight > rightHight ?(leftHight+1) : (rightHight+1);
           }
    
       }
    
       public TreeNode getRoot() {
           return root;
       }
    
       public void setRoot(TreeNode root) {
           this.root = root;
       }
    
    
    
    }
    
    
    二叉树相关的遍历
    
    package data.structure.binarytree.algorithm;
    
    import data.structure.binarytree.model.BinaryTree;
    import data.structure.binarytree.model.TreeNode;
    
    import java.util.*;
    
    public class BinaryTreeTraversal {
    
    
        /**
         * 先序遍历(递归) 根结点 ---> 左子树 ---> 右子树
         *
         * @param root
         */
        public void preorderTraversalRecursion(TreeNode root) {
    
            if (root != null) {
                System.out.println(root.getValue() + " ");
                preorderTraversalRecursion(root.getLeftChild());
                preorderTraversalRecursion(root.getRightChild());
            }
    
        }
    
        /**
         * 先序遍历(非递归) 根结点 ---> 左子树 ---> 右子树
         *
         * @param root
         */
        public void preorderTraversal(TreeNode root) {
            Stack<TreeNode> tempNode = new Stack<>();
            TreeNode currentNode = root;
    
            while (currentNode != null || !tempNode.isEmpty()) {
    
                //左子树
                while (currentNode != null) {
                    System.out.println(currentNode.getValue());
                    tempNode.push(currentNode);
                    currentNode = currentNode.getLeftChild();
                }
                //右子树
                currentNode = tempNode.pop().getRightChild();
            }
    
        }
    
        /**
         * 中序遍历(递归) 左子树---> 根结点 ---> 右子树
         *
         * @param root
         */
        public void inorderTraversalRecursion(TreeNode root) {
    
            if (root != null) {
                inorderTraversalRecursion(root.getLeftChild());
                System.out.println(root.getValue() + " ");
                inorderTraversalRecursion(root.getRightChild());
            }
    
        }
    
        /**
         * 中序遍历(递归) 左子树---> 根结点 ---> 右子树
         *
         * @param root
         */
        public void inorderTraversal(TreeNode root) {
    
            Stack<TreeNode> tempNode = new Stack<>();
            TreeNode currentNode = root;
    
            while (currentNode != null || !tempNode.isEmpty()) {
    
                while (currentNode != null) {
                    tempNode.push(currentNode);
                    currentNode = currentNode.getLeftChild();
                }
                TreeNode popNode = tempNode.pop();
                System.out.println(popNode.getValue());
                currentNode = popNode.getRightChild();
            }
    
        }
    
        /**
         * 后序遍历(递归) 左子树 ---> 右子树 ---> 根结点
         *
         * @param root
         */
        public void postorderTraversalRecursion(TreeNode root) {
    
            if (root != null) {
                postorderTraversalRecursion(root.getLeftChild());
                postorderTraversalRecursion(root.getRightChild());
                System.out.println(root.getValue() + " ");
            }
    
        }
    
        /**
         * 后序遍历(非递归) 左子树 ---> 右子树 ---> 根结点
         *
         * @param root
         */
        public static void postorderTraversal(TreeNode root) {
    
            Stack<TreeNode> tempNode = new Stack<>();
            TreeNode currentNode = root;
            TreeNode preNode = null;
    
    
            while (currentNode != null || !tempNode.isEmpty()) {
    
                //左子树
                while (currentNode != null) {
                    tempNode.push(currentNode);
                    currentNode = currentNode.getLeftChild();
                }
                TreeNode popNode = tempNode.pop();
                currentNode = popNode.getRightChild();
                //当前节点右子树不为空(1.还未遍历右子树;2.已经遍历过右子树了,通过preNode是否与当前右子树根结点相同来判断)
                if (currentNode != null && preNode != popNode.getRightChild()) {
                    tempNode.push(popNode);
                } else {
                    System.out.println(popNode.getValue());
                    //记录打印的值
                    preNode = popNode;
                    currentNode = null;
                }
            }
    
        }
    
        /**
         * 后序遍历(递归) 左子树 ---> 右子树 ---> 根结点
         *
         * @param root
         */
        public void maxPathNum(TreeNode root) {
    
            if (root != null) {
                maxPathNum(root.getLeftChild());
                maxPathNum(root.getRightChild());
                System.out.println(root.getValue());
    
            }
        }
    
        private int  maxSum = Integer.MIN_VALUE;
    
        /**
         * 给你一个二叉树的根节点 root ,返回其最大路径和。
         *
         * @param root
         * @return
         */
        public  int maxPathSum(TreeNode root) {
            maxGain(root);
            return maxSum;
        }
    
        public  int maxGain(TreeNode node) {
            if (node == null) {
                return 0;
            }
    
            // 递归计算左右子节点的最大贡献值
            // 只有在最大贡献值大于 0 时,才会选取对应子节点
            int leftGain = Math.max(maxGain(node.getLeftChild()), 0);
            int rightGain = Math.max(maxGain(node.getRightChild()), 0);
    
            // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
            int priceNewpath = node.getValue() + leftGain + rightGain;
    
            // 更新答案
            maxSum = Math.max(maxSum, priceNewpath);
    
            // 返回节点的最大贡献值(只能选择一边,对于它的父节点而言,这条路径要么经过当前节点的左孩子,要么经过右孩子)
            return node.getValue() + Math.max(leftGain, rightGain);
        }
    
    //    public  int maxPathSum2(TreeNode root) {
    //        int result = maxPathSumCount(root);
    //        return Math.max(maxSum,result);
    //    }
    //
    //    public  int maxPathSumCount(TreeNode root) {
    //        if (root == null) {
    //            return 0;
    //        }
    //        int leftMax = Math.max(maxPathSumCount(root.getLeftChild()), 0);
    //        int rightMax = Math.max(maxPathSumCount(root.getRightChild()), 0);
    //        maxSum = Math.max((root.getValue() + leftMax + rightMax) , maxSum);
    //        return root.getValue() +  Math.max(leftMax, rightMax);
    //    }
    
    //    public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    //        if (root == null){
    //            return null;
    //        }
    //
    //        Stack<TreeNode> result1 = new Stack<>();
    //        Stack<TreeNode> result2 = new Stack<>();
    //        getAllAncestor(root,p,result1);
    //        getAllAncestor(root,q,result2);
    //
    //
    //            while (result1.size() != result2.size()){
    //                if (result1.size() > result2.size()){
    //                    result1.pop();
    //                }
    //                if (result1.size() < result2.size()){
    //                    result2.pop();
    //                }
    //            }
    //
    //
    //        while (result1.size() > 0){
    //            TreeNode treeNode = result1.pop();
    //            if (treeNode == result2.pop()){
    //                return treeNode;
    //            }
    //        }
    //        return null;
    //    }
    
    //    Stack<TreeNode> result = new Stack<>();
    //    public static TreeNode  getAllAncestor(TreeNode root, TreeNode node,Queue<TreeNode> result){
    //        if (root != null){
    //            if (root.getValue() == node.getValue()){
    //                result.offer(root);
    //                return null;
    //            }
    //            if ((root.getLeftChild()!= null && root.getLeftChild().getValue() == node.getValue() )
    //                    || (root.getRightChild()!= null && root.getRightChild().getValue() == node.getValue())){
    //                result.offer(node);
    //                result.offer(root);
    //                return root;
    //            }
    //            TreeNode node1 = getAllAncestor(root.getLeftChild(),node,result);
    //            if (node1 != null && root.getLeftChild().getValue() == node1.getValue()){
    //                result.offer(root);
    //                return root;
    //            }
    //
    //            TreeNode node2 =getAllAncestor(root.getRightChild(),node,result);
    //            if (node2 != null && root.getRightChild().getValue() == node2.getValue()){
    //                result.offer(root);
    //                return root;
    //            }
    //
    //        }
    //
    //        return null;
    //    }
    
        public static void  getAllAncestor(TreeNode root, TreeNode node,Stack<TreeNode> result){
    
            TreeNode currentNode = root;
            TreeNode preNode = null;
    
    
            while (currentNode != null || !result.isEmpty()) {
    
                //左子树
                while (currentNode != null) {
                    result.push(currentNode);
                    if (currentNode.getValue() == node.getValue()){
                        return;
                    }
                    currentNode = currentNode.getLeftChild();
                }
                TreeNode popNode = result.pop();
                currentNode = popNode.getRightChild();
                //当前节点右子树不为空(1.还未遍历右子树;2.已经遍历过右子树了,通过preNode是否与当前右子树根结点相同来判断)
                if (currentNode != null && preNode != popNode.getRightChild()) {
                    result.push(popNode);
                } else {
    
                    //记录打印的值
                    preNode = popNode;
                    currentNode = null;
                }
            }
        }
    
    
        private TreeNode ans;
    
        private boolean dfs(TreeNode root, TreeNode p, TreeNode q) {
            if (root == null) return false;
            boolean lson = dfs(root.getLeftChild(), p, q);
            boolean rson = dfs(root.getRightChild(), p, q);
            //两种情况:1.p,q分别在该节点的左右子树中;2.p,q其中一个节点本身,另一个节点在其子树上
            if ((lson && rson) || ((root.getValue() == p.getValue() || root.getValue() == q.getValue()) && (lson || rson))) {
                ans = root;
            }
            return lson || rson || (root.getValue() == p.getValue() || root.getValue() == q.getValue());
        }
    
        /**
         * 获取最早公共祖先
         * @param root
         * @param p
         * @param q
         * @return
         */
        public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
            this.dfs(root, p, q);
            return this.ans;
        }
    
    
    
            public static void main(String[] args) {
            //[3,5,1,6,2,0,8,null,null,7,4]
            TreeNode nodea = new TreeNode(3);
            TreeNode nodeb = new TreeNode(5);
            TreeNode nodec = new TreeNode(1);
            TreeNode noded = new TreeNode(6);
            TreeNode nodee = new TreeNode(2);
            TreeNode nodef = new TreeNode(0);
            TreeNode nodeg = new TreeNode(8);
            TreeNode nodeh = new TreeNode(7);
            TreeNode nodei = new TreeNode(4);
            //TreeNode node3 = new TreeNode(3);
    
            nodea.setLeftChild(nodeb);
            nodea.setRightChild(nodec);
            nodeb.setLeftChild(noded);
            nodeb.setRightChild(nodee);
            nodec.setLeftChild(nodef);
            nodec.setRightChild(nodeg);
            nodee.setLeftChild(nodeh);
            nodee.setRightChild(nodei);
    
            TreeNode node5 = new TreeNode(1);
            TreeNode node1 = new TreeNode(5);
    //        TreeNode result = lowestCommonAncestor(nodea,node1,node5);
    //        Stack<TreeNode> result1 = new Stack<>();
    //        getAllAncestor(nodea,node5,result1);
    //        System.out.println(result.getValue());
    //        int a = 1;
    //        int b = 1;
    //        if (a == 1){
    //            System.out.println("aaaaaaaa");
    //        }else if (b == 1){
    //            System.out.println("bbbbbbbb");
    //        }else {
    //            System.out.println("ccccccccc");
    //        }
    //        System.out.println(maxPathSum(nodea));
    //        node1.setRightChild(node2);
    //        node2.setLeftChild(node3);
    //        node2.setRightChild(node4);
    //        postorderTraversal(node1);
    
            //[0,2,4,1,null,3,-1,5,1,null,6,null,8]
    //        BinaryTree tree = new BinaryTree(node0);
    //        System.out.println(tree.hight());
        }
    }
    
    

    leetcode上有相应的题,可以做一下:
    先序遍历:在做这个题如果使用递归算法的话,需要注意要将存储结果的list传入到递归循环中,
    中序遍历:同上。
    后序遍历:同上。
    最大深度:直接使用BinaryTree类中获取树高的方法,一个递归的是深度优先遍历,一个非递归是广度优先遍历。
    是否平衡二叉树判断:我在二叉树中的代码写了一个自底向上的判断方法(isBalanced),时间复杂度为n,还可以自顶向下遍历,对每个节点的左右子树的高度进行比较(主要利用计算树高的方法),时间复杂度为n的二次方;
    计算root节点最大路径和:这个题我觉得难在理解上,对于当前节点(node)而言,最大路径和只有四种情况:(最大贡献值怎么理解:空节点的最大贡献值等于 0;非空节点的最大贡献值等于节点值加上左右节点中较大贡献值的节点的贡献值;对于叶节点而言,最大贡献值等于节点值)有点绕,慢慢理解吧
    1.node本身的value;
    2.左右节点的最大贡献值都大于0--node.value+left.value+right.value;
    3.左节点最大贡献值大于0,右节点小于0--node.value+left.value;
    4.左节点最大贡献值小于0,右节点大于0--node.value+left.value
    二叉树的最近公共祖先:这个题我自己写了递归和非递归的代码,思路是用栈或者队列按顺序储存两个节点的祖先节点(包含自己本身),然后将较长的队列或者栈的出队或者出栈到较小长度的队列或者栈的长度,然后一起出队或者出栈,相同的第一个相同的节点就是最近公共祖先,但是结果不尽人意,递归的方法比官方的递归方法多一毫秒,所以我还是注释掉了自己的代码,留下了官方的代码:

       private TreeNode ans;
    
        private boolean dfs(TreeNode root, TreeNode p, TreeNode q) {
            if (root == null) return false;
            boolean lson = dfs(root.getLeftChild(), p, q);
            boolean rson = dfs(root.getRightChild(), p, q);
            //两种情况:1.p,q分别在该节点的左右子树中;2.p,q其中一个节点本身,另一个节点在其子树上
            if ((lson && rson) || ((root.getValue() == p.getValue() || root.getValue() == q.getValue()) && (lson || rson))) {
                ans = root;
            }
            return lson || rson || (root.getValue() == p.getValue() || root.getValue() == q.getValue());
        }
    
        /**
         * 获取最早公共祖先
         * @param root
         * @param p
         * @param q
         * @return
         */
        public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
            this.dfs(root, p, q);
            return this.ans;
        }
    
    

    二叉树的层序遍历:最大深度的时候已经写过这个方法了,稍微修改一下就可以了。

    public List<List<Integer>> levelOrder(TreeNode root) {
            if (root == null){
                return new ArrayList<>();
            }
    
            List<List<Integer>> result = new ArrayList<>();
            Queue<TreeNode> tempNode = new LinkedList<>();
            tempNode.offer(root);
            while (!tempNode.isEmpty()){
                int count = tempNode.size();
                List<Integer> tempList = new ArrayList<>();
    
                while (count > 0){
                    root = tempNode.poll();
                    tempList.add(root.val);
                    if (root.left != null){
                        tempNode.offer(root.left);
                    }
                    if (root.right != null){
                        tempNode.offer(root.right);
                    }
                    count--;
                }
                result.add(tempList);
            }
    
            return result;
        }
    

    二叉树的层序遍历 II:这个也是层序遍历,不过是结果逆序输出,我想到办法是使用栈,最后遍历一栈,把结果输出出来,不过官方的方案更好一点,利用了链表头插法的为常量复杂度的性质,比我的方法更快:

    public List<List<Integer>> levelOrderBottom(TreeNode root) {
            List<List<Integer>> levelOrder = new LinkedList<List<Integer>>();
    
            if (root == null){
                return levelOrder;
            }
            Queue<TreeNode> tempNode = new LinkedList<>();
            tempNode.offer(root);
            while (!tempNode.isEmpty()){
                int count = tempNode.size();
                List<Integer> tempList = new ArrayList<>();
    
                while (count > 0){
                    root = tempNode.poll();
                    tempList.add(root.val);
                    if (root.left != null){
                        tempNode.offer(root.left);
                    }
                    if (root.right != null){
                        tempNode.offer(root.right);
                    }
                    count--;
                }
                levelOrder.add(0,tempList);//头插法
            }
            
            return levelOrder;
        }
    

    二叉树的锯齿形层序遍历:依旧是层序遍历,我吸取了上道题利用链表头插的特性,而官方则是利用双向队列,我们都使用一个标识来判断是顺序还是逆序,每遍历一层就反转一下或者利用层数奇偶性

    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        if (root == null){
                return new ArrayList<>();
            }
    
            List<List<Integer>> result = new ArrayList<>();
            Queue<TreeNode> tempNode = new LinkedList<>();
            tempNode.offer(root);
            boolean isOrderLeft = false;
     
            while (!tempNode.isEmpty()){
                int count = tempNode.size();
                List<Integer> tempList = new  LinkedList<>();
                
                while (count > 0){
                    root = tempNode.poll();
                    if(isOrderLeft){
                        tempList.add(0,root.val);
                    }else{
                        tempList.add(root.val);
                    }            
                    if (root.left != null){
                        tempNode.offer(root.left);
                    }
                    if (root.right != null){
                        tempNode.offer(root.right);
                    }
                    count--;
                }
                isOrderLeft = !isOrderLeft;
                result.add(tempList);
            }
    
            return result;
        }
    

    验证二叉搜索树:通过不断缩小边界来判断,对于root节点,边界为正负无穷,对于root的左子树而言,边界为负无穷到root.val,对于右子树而言,边界为root.val到正无穷。

    /**
         * 验证是否是搜索二叉树(不断缩小边界来判断)
         * @param root
         * @return
         */
        public static boolean isValidBST(TreeNode root) {
            if (root == null){
                return true;
            }
    
            return isValidBST(root,Integer.MAX_VALUE,Integer.MIN_VALUE);
    
        }
    
        public static boolean isValidBST(TreeNode root,int maxValue,int minValue) {
            if (root == null) {
                return true;
            }
    
            if (root.getValue() <= minValue || root.getValue() >= maxValue){
                return false;
            }
    
            return isValidBST(root.getLeftChild(),maxValue,root.getValue())&&isValidBST(root.getRightChild(),root.getValue(),minValue);
        }
    

    二叉搜索树中的插入操作:

    /**
         * 插入搜索二叉树
         * @param root
         * @param val
         * @return
         */
        public TreeNode insertIntoBST(TreeNode root, int val) {
            if (root == null){
                return new TreeNode(val);
            }
    
            if (val < root.getValue()){
                root.setLeftChild(insertIntoBST(root.getLeftChild(),val));
            }else{
                root.setRightChild(insertIntoBST(root.getRightChild(),val));
    
            }
    
            return root;
        }
    

    相关文章

      网友评论

          本文标题:leetcode科学刷题(1)-二叉树(数据结构篇)

          本文链接:https://www.haomeiwen.com/subject/fshgdktx.html