【剑指offer】Java:树


参考了很多大佬的题解,仅作为自己学习笔记用。


07. 给前中序重建二叉树

题意:

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
在这里插入图片描述
题解:

要先将中序放入哈希表。部分递归示意图:
在这里插入图片描述

class Solution {
    int[] pre;
    HashMap<Integer, Integer> map = new HashMap<>();

    public TreeNode buildTree(int[] pre, int[] in) {
        this.pre = pre;
        for(int i = 0; i < in.length; i++)		// 将中序放入map集合
            map.put(in[i], i);
        return recur(0, 0, in.length - 1);
    }
	// 三个形参的含义:根在前序中的索引,子树在中序的左边界,子树在中序的右边界
    public TreeNode recur(int root, int left, int right) {
        if(left > right) 
            return null;                                        // 递归终止
        TreeNode node = new TreeNode(pre[root]);                // 建立根节点
        int i = map.get(pre[root]);  // 拿出根节点在中序map的索引,划分根节点、左子树、右子树
        node.left = recur(root + 1, left, i - 1);              // 开启左子树递归
        node.right = recur(i - left + root + 1, i + 1, right); // 开启右
        return node;                                           // 回溯返回根节点
    }
}

08. 二叉树的下一个节点

题意:(leetcode剑指专题没有,做的牛客)

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}

题解:

一共有三种情况:
1、给的节点有右子树,那么下一个节点就是右子树的最左节点。
在这里插入图片描述
2、给的节点无右子树,并且自身是右节点,那么就一直往上找,找到一个节点是左节点,那么这个左节点的父亲就是下一节点。
在这里插入图片描述
3、给的节点无右子树,并且自身是左节点,那么下一节点就是他的父节点。(中序遍历:左根右)

    public TreeLinkNode GetNext(TreeLinkNode pNode){
        if(pNode.right != null){			// 情况1:有右子树,找右子树的最左边节点
            TreeLinkNode node = pNode.right;
            while(node.left != null)
                node = node.left;
            return node;
        }else{								// 情况2&3:没有右子树
            while(pNode.next != null){
                TreeLinkNode parent = pNode.next;
                if(parent.left == pNode)	// 情况3:自身是左节点
                    return parent;
                pNode = parent;				// 情况2:自身是右节点
            }
        }
        return null;
    }

26. 树的子结构

题意:

输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(约定空树不是任意一个树的子结构)
B 是 A 的子结构, 即 A 中有出现和 B 相同的结构和节点值。
在这里插入图片描述
题解:

    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A == null || B == null)	return false;

        //先从根节点判断B是不是A的子结构,如果不是在分别从左右两个子树判断,
        return recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
    }

    public boolean recur(TreeNode A, TreeNode B){
        if(B == null)	return true; //这里如果B为空,说明B已经访问完了,确定是A的子结构
        
        //如果B不为空A为空,或者这两个节点值不同,说明B树不是A的子结构
        if(A == null || A.val != B.val)
            return false;           
        //说明当前节点B是A的子,当前节点比较完之后还要继续判断左右子节点
        return recur(A.left, B.left) && recur(A.right, B.right);
    }

27. 二叉树的镜像

题意:

请完成一个函数,输入一个二叉树,该函数输出它的镜像。
在这里插入图片描述
题解:

交换每个节点的左右节点。

    public TreeNode mirrorTree(TreeNode root) {
        if(root == null) return null;

        TreeNode left = root.left;
        root.left = mirrorTree(root.right);
        root.right = mirrorTree(left);
        return root;
    }

28. 对称的二叉树

题意:

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
在这里插入图片描述
题解:

    public boolean isSymmetric(TreeNode root) {
        if(root == null) return true;

        return recur(root.left, root.right);
    }

    public boolean recur(TreeNode l, TreeNode r){
        if(l == null && r == null)      // 同时越过叶子节点,true
            return true;
        if(l == null || r == null)      // 只有其中一个越过叶子节点,false
            return false;
        if(l.val != r.val)              // 值不相等,false
            return false;

        return recur(l.left, r.right) && recur(l.right, r.left);
    }

32-1. 从上到下打印二叉树(一行)

题意:

从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
在这里插入图片描述
题解:

层序遍历,使用队列。先将根节点加入队列,然后访问,再加入左右子节点。

    public int[] levelOrder(TreeNode root) {
        if(root == null)	return new int[0];

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);                // 父节点先加入队列
        ArrayList<Integer> list = new ArrayList<>();

        while(!queue.isEmpty()){        // 队列不空时
            TreeNode node = queue.poll(); // 弹出队首
            list.add(node.val);            // 加入list
            if(node.left != null)          // 加入刚刚弹出的左右节点
                queue.add(node.left);
            if(node.right != null) 
                queue.add(node.right);
        }

        int[] res = new int[list.size()];
        for(int i=0; i<list.size(); i++)
            res[i] = list.get(i);
        return res;
    }

32-2. 从上到下打印二叉树(多行)

题目和上一题一样,只不过这一次打印的一层就是一行,需要打印多行。

    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()) {
            List<Integer> list = new ArrayList<>();
            int size = queue.size();        // 统计队列数量,为 0 的时候就需要换行,加入list
            while(size-- > 0){
                TreeNode node = queue.poll();
                list.add(node.val);
                if(node.left != null) 
                    queue.add(node.left);
                if(node.right != null) 
                    queue.add(node.right);
            }
            res.add(list);
        }
        return res;
    }

32-3. 从上到下打印二叉树(之字型)

题目和上一题一样,只不过这一次打印的一层就是一行,需要打印多行,同时蛇形走位。
在这里插入图片描述

    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()) {
            List<Integer> list = new ArrayList<>();
            int size = queue.size();
            while(size-- > 0){
                TreeNode node = queue.poll();
                list.add(node.val);
                if(node.left != null) 
                    queue.add(node.left);
                if(node.right != null) 
                    queue.add(node.right);
            }
            if(res.size() % 2 == 1){   // 如果是奇数行,需要反转链表,第一行是0,第二行是1
                Collections.reverse(list);
            }
            res.add(list);
        }
        return res;
    }

33. 二叉搜索树的后序遍历序列

题意:

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
在这里插入图片描述
题解:

class Solution {
    public boolean verifyPostorder(int[] postorder) {
        return recur(postorder, 0, postorder.length - 1);
    }

    boolean recur(int[] postorder, int left, int right) {
        //如果left==right,就一个节点不需要判断了,如果left>right说明没有节点,
        //也不用再看了,否则就要继续往下判断
        if (left >= right)
            return true;
        //因为数组中最后一个值postorder[right]是根节点,这里从左往右找出第一个比
        //根节点大的值,他后面的都是根节点的右子节点(包含当前值,不包含最后一个值,
        //最后一个是根节点),他前面的都是根节点的左子节点
        int mid = left;
        int root = postorder[right];
        while (postorder[mid] < root)
            mid++;
        int temp = mid;
        //因为postorder[mid]前面的值都是比根节点root小的,
        //我们还需要确定postorder[mid]后面的值都要比根节点root大,
        //如果后面有比根节点小的直接返回false
        while (temp < right) {
            if (postorder[temp++] < root)
                return false;
        }
        //然后对左右子节点进行递归调用
        return recur(postorder, left, mid - 1) && recur(postorder, mid, right - 1);
    }
}

34. 二叉树中和位某一值的路径

题意:

输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
在这里插入图片描述
题解:

class Solution {
    LinkedList<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>();

    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        recur(root, sum);
        return res;
    }

    public void recur(TreeNode node, int tar) {
        if(node == null) 
            return;
            
        path.add(node.val);
        tar -= node.val;
        if(tar == 0 && node.left == null && node.right == null)
            res.add(new LinkedList(path));
        recur(node.left, tar);
        recur(node.right, tar);
        path.removeLast();
    }
}

36. 二叉搜索树和双向链表

题意:

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
在这里插入图片描述
在这里插入图片描述
题解:

class Solution {
    Node pre, head;
    public Node treeToDoublyList(Node root) {
        if(root == null) return null;
        recur(root);
        head.left = pre;		// 首尾相连,因为是循环双向链表,不写这个就是纯双向链表
        pre.right = head;
        return head;
    }

    public void recur(Node cur) {
        if(cur == null)	return;		// 递归终止条件
        
        recur(cur.left);			// 左节点
        if(pre != null)				// 根节点,如果pre 遍历到末端
            pre.right = cur;
        else 
            head = cur;
        cur.left = pre;
        pre = cur;
        recur(cur.right);			// 右节点
    }
}

37. 序列化二叉树

题意:

请实现两个函数,分别用来序列化和反序列化二叉树。
在这里插入图片描述
题解:

public class Codec {
    public String serialize(TreeNode root) {
        if(root == null) return "[]";
        StringBuilder res = new StringBuilder("[");
        Queue<TreeNode> queue = new LinkedList<>() {{ add(root); }};
        while(!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if(node != null) {
                res.append(node.val + ",");
                queue.add(node.left);
                queue.add(node.right);
            }
            else
                res.append("null,");
        }
        res.deleteCharAt(res.length() - 1);
        res.append("]");
        return res.toString();
    }

    public TreeNode deserialize(String data) {
        if(data.equals("[]")) return null;
        String[] vals = data.substring(1, data.length() - 1).split(",");
        TreeNode root = new TreeNode(Integer.parseInt(vals[0]));
        Queue<TreeNode> queue = new LinkedList<>() {{ add(root); }};
        int i = 1;
        while(!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if(!vals[i].equals("null")) {
                node.left = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(node.left);
            }
            i++;
            if(!vals[i].equals("null")) {
                node.right = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(node.right);
            }
            i++;
        }
        return root;
    }
}

54. 二叉搜索树的第 k 大节点

题意:

给定一棵二叉搜索树,请找出其中第k大的节点。

在这里插入图片描述
题解:

class Solution {
    private int count = 0;
    private int res;

    public int kthLargest(TreeNode root, int k) {
        if(root == null || k == 0)  return 0;
        
        inorder(root, k);
        return res;
    }

    public void inorder(TreeNode root, int k){
        if(root == null || count >= k)  return;

        inorder(root.right, k);
        count++;
        if(count == k){
            res = root.val;
            return;
        }
        inorder(root.left, k);
    }
}

55-1. 二叉树的深度

题意:

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
在这里插入代码片在这里插入图片描述
题解:

    public int maxDepth(TreeNode root) {
        return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }

55-2. 判断平衡二叉树

题意:

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
在这里插入图片描述
题解:

class Solution {
    private boolean isBalanced = true;

    public boolean isBalanced(TreeNode root) {
        height(root);
        return isBalanced;
    }
    public int height(TreeNode root){
        if(root == null || !isBalanced)
            return 0;
        int left = height(root.left);
        int right = height(root.right);
        if(Math.abs(left - right) > 1)
            isBalanced = false;
        return 1 + Math.max(left, right);
    }
}

68-1. 二叉搜索树的最近祖先

题意:

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

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
在这里插入图片描述
题解1:

迭代法,经过改进的。

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(p.val > q.val) { 			// 改进一下,保证 p.val < q.val
            TreeNode tmp = p;
            p = q;
            q = tmp;
        }
        while(root != null) {
            if(root.val < p.val) 		// p,q 都在 root 的右子树中
                root = root.right; 		// 遍历至右子节点
            else if(root.val > q.val) 	// p,q 都在 root 的左子树中
                root = root.left; 		// 遍历至左子节点
            else break;
        }
        return root;
    }

题解2:

递归。

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root.val < p.val && root.val < q.val)
            return lowestCommonAncestor(root.right, p, q);
        if(root.val > p.val && root.val > q.val)
            return lowestCommonAncestor(root.left, p, q);
        return root;
    }

68-2. 二叉树的最近祖先

和上一题一样,只不过二叉搜索树变成了二叉树。

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null; // 如果树为空,直接返回null

        // 如果 p和q中有等于 root的,那么它们的最近公共祖先即为root(一个节点也可以是它自己的祖先)
        if(root == p || root == q) return root;
        // 递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁
        TreeNode left = lowestCommonAncestor(root.left, p, q); 
        // 递归遍历右子树,只要在右子树中找到了p或q,则先找到谁就返回谁
        TreeNode right = lowestCommonAncestor(root.right, p, q); 
        // 如果在左子树中 p和 q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        if(left == null) 
            return right;
        else if(right == null)
            return left; // 否则,如果 left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况,如果在右子树中,p和q都找不到,则 p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        else
            return root; //否则,当 left和 right均不为空时,说明 p、q节点分别在 root异侧, 最近公共祖先即为 root
    }

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值