一文通数据结构与算法之——二叉树+常见题型与解题策略+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. 对称的二叉树

image-20210920172828991

//用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
//递归法
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 || rightHeight-leftHeight>1){
                    return false;
                }else{
                    return isBalanced(root.left)&&isBalanced(root.right);
                }
            }
        }

        BST——> AVL

剑指 Offer 55 - I. 二叉树的深度
//    1.递归+1的深度
//    2.辅助栈
int res=1;
public int maxDepth(TreeNode root) {
    if(root==null){
        return 0;
    }
    return res+Math.max(maxDepth(root.left),maxDepth(root.right));
}

public int maxDepth(TreeNode root){
        int count = 0;
        if(root==null) return count;
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
//            要维护深度
            int size = queue.size();
            while(size>0){
                TreeNode node = queue.poll();
                if(node.left!=null){
                    queue.offer(node.left);
                }
                if(node.right!=null){
                    queue.offer(node.right);
                }
                size--;
            }
            count++;
        }
        return count;
    }
剑指 Offer 55 - II. 平衡二叉树
//输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
 //1.递归
    public boolean isBalanced(TreeNode root) {
        if(root==null) return true;
        int leftDepth = depth(root.left);
        int rightDepth = depth(root.right);
        return Math.abs(leftDepth-rightDepth)<=1 &&
                isBalanced(root.left) && isBalanced(root.right);
    }

    public int depth(TreeNode node){
//        基线,为空
        if(node==null) return 0;
        int left = depth(node.left);
        int right = depth(node.right);
        return Math.max(left,right)+1;
    }

剑指 Offer 32 - I. 从上到下打印二叉树
public int[] levelOrder(TreeNode root) {
    if(root==null) return null;
    Queue<TreeNode> queue = new LinkedList<>();
    ArrayList<Integer> list = new ArrayList<>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        TreeNode node = queue.poll();
        list.add(node.val);
        if(node.left!=null) queue.offer(node.left);
        if(node.right!=null) queue.offer(node.right);
    }
    int[] ans = new int[list.size()];
    int i = 0;
    for (Integer integer : list) {
        ans[i++]=(integer);
    }
    return ans;
}
剑指 Offer 32 - II. 从上到下打印二叉树 II
	public List<List<Integer>> levelOrder(TreeNode root) {
        if(root==null) return new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> list = new ArrayList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            ArrayList<Integer> temp = new ArrayList<>();
            while(size>0){
                TreeNode node = queue.poll();
                temp.add(node.val);
                if(node.left!=null) queue.offer(node.left);
                if(node.right!=null) queue.offer(node.right);
                size--;
            }
            list.add(temp);
        }
        return list;
    }
剑指 Offer 32 - III. 从上到下打印二叉树 III
//请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印
 	public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<>();
        if(root != null) queue.add(root);
        while(!queue.isEmpty()) {
            LinkedList<Integer> tmp = new LinkedList<>();
            for(int i = queue.size(); i > 0; i--) {
                TreeNode node = queue.poll();
                //充分利用LinkedList是链表的特征。可以双向进行添加
                if(res.size() % 2 == 0) tmp.addLast(node.val); // 偶数层 -> 队列头部
                else tmp.addFirst(node.val); // 奇数层 -> 队列尾部
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            res.add(tmp);
        }
        return res;
    }
剑指 Offer 07. 重建二叉树
	int[] preorder;
    Map<Integer,Integer> map;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder = preorder;
        map = new HashMap<>();//1.为方便根据值来get到索引,将数据存到map中
        for (int i = 0; i < preorder.length; i++) {
            map.put(inorder[i],i);
        }
        return build(0,0,preorder.length-1);//第一个参数preOrder用,第二个和第三个是inorder用来确定左子树右子树的边界
    }

    public TreeNode build(int rootIndex,int left,int right){
        if(left>right) return null;//1.递归的基线
        TreeNode root = new TreeNode(preorder[rootIndex]);
        int split = map.get(preorder[rootIndex]);
        
        root.left = build(rootIndex+1,left,split-1);
        root.right = build(rootIndex+split-left+1,split+1,right);//如果直接写成split+1,会出现数组越界
        return root;//        如:[1 2 3],[3 2 1]和[1 2 3],[2 3 1]这两种情况
    }
剑指 Offer 37. 序列化二叉树
 public String serialize(TreeNode root) {
//        前中后序遍历都会缺失,无法完全复原,所以以层序遍历的形式来
        if(root==null)return "[]";
        Queue<TreeNode> queue = new LinkedList<>();
        StringBuilder str = new StringBuilder("[");
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            if(node != null) {
                str.append(node.val + ",");
                queue.add(node.left);
                queue.add(node.right);
            }
            else str.append("null,");
        }
        str.deleteCharAt(str.length() - 1);
        str.append("]");
        return str.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.equals("[]")) return null;
//        1.将字符数组还原成数值数组
        String[] list = data.substring(1,data.length()-1).split(",");
        TreeNode root = new TreeNode(Integer.parseInt(list[0]));
//        老老实实用队列呗
        Queue<TreeNode> queue= new LinkedList<>();
        int i = 1;
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
//            构建左子树
            if(!list[i].equals("null")){
                node.left = new TreeNode(Integer.parseInt(list[i]));
                queue.offer(node.left);
            }
            i++;
//            构建右子树
            if(!list[i].equals("null")){
                node.right = new TreeNode(Integer.parseInt(list[i]));
                queue.offer(node.right);
            }
            i++;
        }
        return root;
    }
剑指Offer 36:二叉搜索树与双向链表
强化一下递归子树的操作,假装理解为递归的操作已经完全ok了,直接使用结果
//    中序遍历递归写法
    public void inOrder(TreeNode root){
        if(root==null) return ;
//        递归左子树,那么就可以直接理解为,左边已经好了
        inOrder(root.left);
        System.out.println(root);
//        递归右子树,也可以理解为直接好了
        inOrder(root.right);
    }
//    head用于记录头结点
//    pre用于记录当前节点的左子节点
    TreeNode pre,head;
    public TreeNode treeToDoublyList(TreeNode root) {
        if(root==null) return null;
//        1.变换双向链表的操作
        dfs(root);
//        2.头尾指针进行指向,此时head已经固定,尾节点就是pre
        pre.right = head;
        head.left = pre;

        return head;
    }

    public void dfs(TreeNode cur){
        if(cur==null) return;
//        1.左子树ok
        dfs(cur.left);

//        2.操作中间节点,
        if(pre==null){//一直递归左子树,到达左子叶子节点
            head = cur;//固定头结点
        }else{
            pre.right  = cur;
        }
        cur.left = pre;
        pre = cur;

//        3.右子树ok
        dfs(cur.right);
    }

剑指Offer 54:二叉搜索树的第k个结点
//    1.中序遍历反着来,形成倒序数组,计数--为0直接返回
    int count,res;
    public int kthLargest(TreeNode root, int k) {
        this.count = k;
        dfs(root);
        return res;
    }
    public void dfs(TreeNode node){
        if(node==null || count==0) return;
        dfs(node.right);
        if(--count==0){
            res = node.val;
            return ;
        }
        dfs(node.left);
    }
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if(root==null) return null;
    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;
}

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    //1.保证p是节点大的那个
    if(p.val<q.val){
        TreeNode temp = p;
        p = q;
        q = temp;
    }
    while(root!=null){
        if(root.val>p.val) root = root.left;//比大的还大
        else if(root.val<q.val) root = root.right;//比小的还小
        else break;
    }
    return root;
}
剑指 Offer 68 - II. 二叉树的最近公共祖先(注意此时的返回扩展到了二叉树)
//方法·一:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) return root;
//        判断是否在左子树
        TreeNode left = lowestCommonAncestor(root.left, p, q);
//        判断是否在右子树
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left == null) return right;
        if (right == null) return left;
        else return root;
    }


//方法二:自己想
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(isChild(p,q)) return p;
        else if(isChild(q,p)) return q;
//        1.当两节点在root的同一侧但是不为子树关系时。返回的应该是root的子节点而不是root
        else if(isChild(root.left,p)&& isChild(root.left,q)){
            return lowestCommonAncestor(root.left,p,q);
        }else if(isChild(root.right,p)&& isChild(root.right,q)){
            return lowestCommonAncestor(root.right,p,q);
        }else return root;
    }

    //判断node是否为p的父节点
    public boolean isChild(TreeNode node,TreeNode p){
        if(node==null) return false;
        if(node.val==p.val ) return true;
        return isChild(node.left,p)||isChild(node.right,p);
    }
剑指 Offer 34. 二叉树中和为某一值的路径————回溯思想

回溯是一种算法思想,可以用递归实现。从问题的某一种可能出发, 搜索从这种情况出发所能达到的所有可能, 当这一条路走到” 尽头 “的时候, 再倒回出发点, 从另一个可能出发, 继续搜索. 这种不断” 回溯 “寻找解的方法, 称作” 回溯法 “。——八皇后问题

递归是一种算法结构,递归会出现在子程序中自己调用自己或间接地自己调用自己。最直接的递归应用就是计算连续数的阶乘,计算规律:n!=(n-1)!*n。

//输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。
	LinkedList<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        recur(root,target);
        return res;
    }
    public void recur(TreeNode node, int target) {
        if(node==null) return;
//        先将当前节点加入路径
        path.add(node.val);
        target-=node.val;
//        1.找到了,入结果
        if(target==0 && node.left==null&&node.right==null){
//            要保留原来的路径没有被改变,所以新建
            res.add(new LinkedList<>(path));
        }
//        2.没找到递归
        recur(node.left,target);
        recur(node.right,target);
//        3.找不到,回溯回到了上一层的recur,继续找其他节点。
//        很强!很难理解!
        path.removeLast();
    }

3 其他

3.二叉树的递归解决问题
|-- 最大深度、最小深度、最大宽度、节点个数、二叉树的直径

    二叉树的最小深度
    public int minDepth(TreeNode root) {
    if(root == null)
        return 0;
    int left = minDepth(root.left);
    int right = minDepth(root.right);
    //因为我们不知道left还是right的长度是0,所以此时用左加右加1!
    return (left == 0 || right == 0) ? left + right + 1 : Math.min(left, right) + 1;
}

二叉树的最大宽度:
    每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。

    public int widthOfBinaryTree(TreeNode root) {
    Queue< PositionTreeNode> queue = new LinkedList();
    int dep = 0, left = 0, ans = 0;
    queue.offer(new PositionTreeNode(root,0,0));
    while(!queue.isEmpty()){
        PositionTreeNode treeNode = queue.poll();
        //根据满二叉树的特点来标记位置
        if(treeNode.node!=null){
            queue.offer(new PositionTreeNode(treeNode.node.left,treeNode.depth+1,treeNode.pos*2));
            queue.offer(new PositionTreeNode(treeNode.node.right,treeNode.depth+1,treeNode.pos*2+1));

            //刚进入下一层时,需要重置最左端节点的位置。
            //第一个dep不等于node.dep就说明是第一个进入下一层的节点
            //也就是此节点的pos位置就是最左端的节点位置
            if(dep!=treeNode.depth){
                dep = treeNode.depth;
                left = treeNode.pos;
            }
            ans = Math.max(ans,treeNode.pos-left+1);
        }
    }
    return ans;
}

class  PositionTreeNode {
    TreeNode node;
    int depth, pos;
    PositionTreeNode(TreeNode n, int d, int p) {
        node = n;
        depth = d;
        pos = p;
    }
}


二叉树的节点个数:
    public int getNodeNumRec(TreeNode root) {
    if (root == null) {
        return 0;
    }             
    return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;
}

4.完全二叉树(叶子节点都在左边)的节点个数
    二叉树的基本概念和类型:https://www.cnblogs.com/sunshineliulu/p/7775063.html

public int countNodes(TreeNode root) {
    if(root==null){
        return 0;
    }
    int leftDepth = nodeCount(root.left);
    int rightDepth = nodeCount(root.right);

    if(leftDepth ==rightDepth){
        return (1<<leftDepth) + countNodes(root.right);
    }else{
        return (1<<rightDepth) + countNodes(root.left);
    }
}

public int nodeCount(TreeNode node){
    int depth=0;
    while(node!=null){
        depth++;
        node = node.left;
    }
    return depth;
}

二叉树的直径:任意两个结点路径长度中的最大值
    int num = 0;
public int diameterOfBinaryTree(TreeNode root){
    depth(root);
    return num;
}
public int depth(TreeNode node){
    if(node==null){
        return 0;
    }else{
        int leftdepth = depth(node.left);
        int rightdepth = depth(node.right);
        num = Math.max(num,leftdepth+rightdepth);//保存的最大的左子树与右子树深度之和就是最大的直径
        return Math.max(leftdepth,rightdepth)+1;//子树的深度就是左子树、右子树中最大值+1
    }
}


|-- 二叉树路径和、最大路径和、第k大的节点、两节点的最大距离

    1.某路径和:
    public boolean hasPathSum2(TreeNode root, int sum) {
    if (root == null) {
        return false;
    } else {
        //广度遍历,保存每一层的和
        //问题在于原地更新新的和值,所以此时不用ArrayList反而用LInkedList
        LinkedList<TreeNode> queue = new LinkedList<>();
        LinkedList<Integer> list = new LinkedList<>();
        queue.offer(root);
        list.offer(root.val);
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            int num = list.poll();
            //如果此处一直poll下面不判断返回,最后所有都会被poll出去没有元素
            if (node.left == null && node.right == null) {
                if (num == sum) {
                    return true;
                }
                continue;
            }

            if (node.left != null) {
                queue.offer(node.left);
                list.offer(node.left.val + num);
            }
            if (node.right != null) {
                queue.offer(node.right);
                list.offer(node.right.val + num);
            }
        }
        return false;
    }
}

2.第k大节点:二叉搜索树中序遍历出来是有序数组
    public int kthLargest2(TreeNode root, int k) {
    ArrayList<Integer> list = new ArrayList<>();
    inOrder2(root,list);
    return list.get(k-1);
}

public void inOrder2(TreeNode node,ArrayList<Integer> list){
    if(node==null){
        return;
    }else{
        inOrder2(node.right,list);
        list.add(node.val);
        inOrder2(node.left,list);
    }
}

|-- 公共祖先
    1.二叉树的公共祖先:思想在判断两个节点是否在同一棵子树
    public TreeNode lowestCommonAncestor236(TreeNode root, TreeNode p, TreeNode q) {
    if(root==null || root==p |root==q){
        return root;
    }
    TreeNode left = lowestCommonAncestor236(root.left,p,q);
    TreeNode right = lowestCommonAncestor236(root.right,p,q);

    if(left == null && right == null) return null;

    if(left==null){
        return right;
    }

    if(right==null){
        return left;
    }

    return root;
}

2.二叉树的公共祖先
    public TreeNode lowestCommonAncestor235(TreeNode root, TreeNode p, TreeNode q) {
    if(p.val<root.val && q.val<root.val){
        return lowestCommonAncestor235(root.left,p,q);
    }
    if(p.val>root.val && q.val>root.val){
        return lowestCommonAncestor235(root.right,p,q);
    }
    return root;
}






|-- N叉树的前、中、后序遍历
N叉树的定义:
class NTreeNode{
    int val;
    List<NTreeNode> children;
    public NTreeNode(int _val, List<NTreeNode> _children) {
        val = _val;
        children = _children;
    }
}

1.前序遍历
 //1.递归法
    public List<Integer> postorder(NTreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null){
            return res;
        }else{
            myposorder(root,res);
            return res;
        }
    }

    private void myposorder(NTreeNode node,List<Integer> res){
        if(node==null){
            return;
        }else{
            for(NTreeNode nodes:node.children){
                myposorder(nodes,res);
            }
            res.add(node.val);
        }
    }
    //2.迭代法
    public List<Integer> postorder2(NTreeNode root) {
        List<Integer> list  = new ArrayList<>();
        if(root==null){
            return list;
        }

        LinkedList<NTreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            NTreeNode node = queue.poll();
            list.add(node.val);
            for (NTreeNode nodes : node.children) {
                queue.offer(nodes);
            }
        }
        Collections.reverse(list);
        return list;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值