剑指OFFER思路总结与代码分享——树篇(Java实现)

剑指OFFER树相关

  • 55-1 二叉树的深度
  • 27 二叉树的镜像
  • 54 二叉搜索树的第K大节点
  • 32-II 从上到下打印二叉树
  • 07 重建二叉树
  • 68-I 二叉搜索树的最近公共祖先
  • 68-II 二叉树的最近公共祖先
  • 32-I 从上到下打印二叉树
  • 32-III 从上到下下打印二叉树
  • 55-II 平衡二叉树
  • 28 对称的二叉树
  • 34 二叉树中和为某一值的路径
  • 37 序列化二叉树
  • 26 树的子结构

注:顺序是先筛选分类再按LeeCode上的通过率排的,每题最后的总结代码都是在LeeCode上跑过的,应该没啥问题。但是思路中的代码都是直接在CSDN编辑器里徒手敲的,若有笔误还烦请告知,蟹蟹~

55-1 二叉树的深度

剑指OFFER思路总结与代码分享——树篇(Java实现)_第1张图片
思路是使用递归,说到递归,立马写下如下思路:

  1. 方法想要干什么?
  2. 方法的终止条件是什么?以此确定终止条件;
  3. 方法的等价关系是什么?以此确定返回值。

立马写下如下三个词:

  • 终止条件
  • 递推关系
  • 返回值

然后开始分析:

  • 首先这个方法想要计算以入参root为根节点的树的深度,那么方法的返回值即为此树的深度
  • 其次这个方法的终止条件是当传入的root为空时,返回深度为0;
  • 最后这个方法的等价关系是root节点的深度等于:root左节点的深度与root右节点的深度相较而言更大的那个深度加上1。

分析结束,将思路转为代码段在草稿纸上写:


终止条件

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

递推关系

depth = Math.max(maxDepth(root.left).maxDepth(root.right)) + 1;

返回值

return dpeth;

整理成代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
    }
}

27 二叉树的镜像

剑指OFFER思路总结与代码分享——树篇(Java实现)_第2张图片
开始分析:

  • 首先这个方法要将以入参root为根节点的树镜像变换;
  • 其次当root为空的时候停止遍历,返回null
  • 最后递归关系是将root左节点的镜像放在右节点上,将root右节点的镜像放在左节点上,即可完成root的镜像。

终止条件

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

递推关系

TreeNode tmp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(tmp);

返回值

return root;

整理成代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode tmp = root.left;
        root.left = mirrorTree(root.right);
        root.right = mirrorTree(tmp);

        return root;
    }
}

54 二叉搜索树的第K大节点

剑指OFFER思路总结与代码分享——树篇(Java实现)_第3张图片
开始分析:

  • 首先这个方法是求第K大个节点对应的值;
  • 其次当传入节点为null时返回,或者找到了第K大节点后返回;
  • 最后递归关系是使用中序遍历的方法遍历这颗二叉搜索树,很关键的一点是看到二叉搜索树首先想到中序遍历,因为二叉搜索树的中序遍历结果是从小到大有序的数组,具体说来就是先看右子树有没有第K大,没有的话看自己是不是第K大,最后再看左子树有没有第K大。

知识点:看到二叉搜索树想到中序遍历


终止条件

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

递推关系

helper(root.right);
if(--count == 0){
	res = root.val;
}
helper(root.left);

返回值

//res直接写在类内上,所有方法共用,不用返回

整理成代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    
    private int count = 0,res = 0;
    public int kthLargest(TreeNode root, int k) {
        count = k;
        helper(root);
        return res;
    }

    private void helper(TreeNode root){
        if(root == null){
            return;
        }
        helper(root.right);
        if(count == 0){
            return;
        }
        if(--count == 0){
            res = root.val;
        }
        helper(root.left);
    }
}

32-II 从上到下打印二叉树

剑指OFFER思路总结与代码分享——树篇(Java实现)_第4张图片
开始分析:
此题需要用到层次遍历,层次遍历也可以被称为二叉树的广度优先遍历BFS,说道广度优先遍历,就要想到我们需要维护一个队列,通过控制节点在队列的进出来控制遍历的节奏。
二叉树BFS的大致框架,更多细节在具体实现的代码注释中有讲到:

//初始化一个队列并把首节点放进队列里
Queue<TreeNode> queue = new LinkedList<>(){{add(root);}};
//当队列不为空的时候循环
while(!queue.isEmpty()){
	//一层层算账
	for(int i = queue.size(); i > 0; i--){
		//先把本层节点给poll出来
		TreeNode node = queue.poll();
		//再把与本层相连的下层左右节点给塞进去
		if(node.left != null){
			queue.add(node.left);
		}
		if(node.right != null){
			queue.add(node.right);
		}
	}
}

知识点:广度优先,先建队列,每层poll出,下层add进。

因为这题打印结果呈现每层单独一个ArrayList的结果,所以要在每层的循环中开一个ArrayList接一下即可。总体框架还是能看出来的。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        //判断特殊情况
        if(root == null){
            return new ArrayList<>();
        }

        //一个ArrayList承载最后的结果
        List<List<Integer>> res = new ArrayList<>();
        //一个Queue->LinkedList承载每一层的节点,初始化为首层的root
        Queue<TreeNode> queue = new LinkedList<>(){{add(root);}};
        
        //若queue为空,则说明所有的节点都处理完毕,可以输出结果了
        //这里用queue.isEmpty(),不能用queue==null
        while(!queue.isEmpty()){
            //缓存每层的数据,这里用List是为了每层直接接上最后的结果
            List<Integer> tmp = new ArrayList<>();
            //一个for,从queue.size()开始,这很关键,
            //用for(int i = 0; i < queue.size(); i++)的话,则会报错
            //因为队列的长度是在变化的,应该首先就指定好,这样才能保证每次循环只出一层的数据
            for(int i = queue.size();i > 0; i--){
                //队列先进先出,进是add出是poll
                TreeNode father = queue.poll();
                //记得先判断是否为空再添加进队列中,不然会报空指针异常
                if(father.left != null){
                    //把左孩子加入进去,等待其在下一层被poll出来
                    queue.add(father.left);
                }
                //右孩子同理
                if(father.right != null){
                    queue.add(father.right);
                }
                //把poll出的节点的值存进ArrayList中
                tmp.add(father.val);
            }
            //把一层的ArrayList值存进最后的结果中
            res.add(tmp);
        }

        return res;
    }
}

07 重建二叉树

剑指OFFER思路总结与代码分享——树篇(Java实现)_第5张图片
先序遍历:根左右
中序遍历:左根右
剑指OFFER思路总结与代码分享——树篇(Java实现)_第6张图片
1:前序遍历的首节点为root

int rootVal = preorder[0];

2:根据拿到的root去中序遍历里寻找对应的root

int rootIndex = 0;
for(int i = 0; i < inorder.length; i++){
	if(rootVal == inorder[i]){
		rootIndex = i;
		break;
	}
}

3-4:在中序遍历中找到root之后,它的左右即为当前root节点的左子树和右子树

//inorder的左边:
Arrays.copyOfRange(inorder, 0, rootIndex);
//inorder的右边:
Arrays.copyOfRange(inorder, rootIndex + 1, inorder.length);

注意copyOfRange的取值范围是左闭右开,可以画图观察,Java里很多这种取值的操作都是左闭右开的,想想str.substring(闭,开);

5-6:根据我们得到的左右子树,去前序遍历找左右子树的段落

//preorder的左边:
Arrays.copyOfRange(preorder, 1, 1 + rootIndex);
//preorder的右边:
Arrays.copyOfRange(preorder, 1 + rootIndex, preorder.length);

7-8:根据前序遍历的特点,我们所找的段落的首节点就是直接连着root的左孩子与右孩子

root.left = buildTree(preorder的左边,inorder的左边);
root.right = buildTree(preorder的右边,inorder的右边);

至此我们就可以进行递归操作了。

有没有发现树的递归操作很多时候都是在root.left和root.right的时候用等于接上的,这种格式可以多多考虑:
root.left = 递归左子树;
root.right = 递归右子树;


整理成代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        if(n == 0){
            return null;
        }
        int rootVal = preorder[0],rootIndex = 0;
        for(int i = 0; i < n; i++){
            if(rootVal == inorder[i]){
                rootIndex = i;
                break;
            }
        }
        TreeNode root = new TreeNode(rootVal);
        root.left = buildTree(Arrays.copyOfRange(preorder,1,1 + rootIndex),Arrays.copyOfRange(inorder,0,rootIndex));
        root.right = buildTree(Arrays.copyOfRange(preorder,1+rootIndex,n),Arrays.copyOfRange(inorder,rootIndex+1,n));

        return root;
    }
}

68-I 二叉搜索树的最近公共祖先

剑指OFFER思路总结与代码分享——树篇(Java实现)_第7张图片

  • 方法的作用是用来找root节点为根节点的树里指定两个节点的公共祖先;
  • 当传入为null时返回;
  • 根据二叉搜索树的性质,若是p与q一个比root大一个比root小,那么就说明root在两个节点之间,否则就在根据root与p、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);
}

返回值

return root;

整理成代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    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);
        }
        return root;

    }
}

68-II 二叉树的最近公共祖先

剑指OFFER思路总结与代码分享——树篇(Java实现)_第8张图片

  • 方法的作用是看p或q在不在,若在则返回p或q,若不在则返回null
  • root == null的时候返回nullroot == p的时候返回proot == q的时候返回q
  • 当左子树为null说明两个都在右边,当右子树为null说明两个都在左边,只有左右子树都不为null的时候该root才是公共祖先,返回这个root,层层上传。

终止条件

if(root == null){
	return null;
}
if(root == p || root == q){
	return root;
}

递推关系

TreeNode leftNode = lowestCommonAncestor(root.left,p,q);
TreeNode rightNode = lowestCommonAncestor(root.right,p,q);
if(leftNode == null){
	return rightNode;
}
if(rightNode == null){
	return leftNode;
}

返回值

return root;

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null){
            return null;
        }
        if(root == p || root == q){
            return root;
        }
        
        TreeNode leftNode = lowestCommonAncestor(root.left,p,q);
        TreeNode rightNode =  lowestCommonAncestor(root.right,p,q);

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

32-I 从上到下打印二叉树

剑指OFFER思路总结与代码分享——树篇(Java实现)_第9张图片
这题为啥会比32-II做出来的人少…我蒙了,就是广度优先遍历的板子就完事了。


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] levelOrder(TreeNode root) {
    	//特殊边界要提前考虑到
        if(root == null){
            return new int[0];
        }
        LinkedList<TreeNode> queue = new LinkedList<>(){{add(root);}};
        ArrayList<Integer> res = new ArrayList<>();
        while(!queue.isEmpty()){
            for(int i = queue.size(); i > 0; i--){
                TreeNode tmp = queue.poll();
                res.add(tmp.val);
                if(tmp.left != null){
                    queue.add(tmp.left);
                }
                if(tmp.right != null){
                    queue.add(tmp.right);
                }
            }
        }
        //这里返回值是int[],再遍历一遍传过去吧,卑微
        int[] n = new int[res.size()];
        for(int j = 0; j < res.size(); j++){
            n[j] = res.get(j);
        }
        return n; 
    }
}

32-III 从上到下下打印二叉树

剑指OFFER思路总结与代码分享——树篇(Java实现)_第10张图片
你要是一路看下来的话这玩意是第三个BFS题了,加个reverse就完事了。这题我们来唠唠返回值的事,这个方法让我们返回的是List>这么个玩意,当root == null的时候,我们应该怎么看返回个啥呢?答案是看它最外层是啥,它最外层是个List<>,那我们就

if(root == null){
	return ArrayList<>();
	//return LinkedList<>();一样也行
}

这玩意说难也不难,就是有时候会一下钻牛角尖里,不造返回啥了,写出来防止忘记。
还有一个好玩的操作是判断奇偶数,这么写其实也不会快(因为听说你用取余的时候,JAVA底层已经优化成这种写法了),但是秀呀哈哈。

//奇数
if((i & 1) == 1){
	...
}
//偶数
if((i & 1) == 0){
	...
}

最后上个代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root == null){
            return new ArrayList<>();
        }
        LinkedList<List<Integer>> res = new LinkedList<>();
        LinkedList<TreeNode> queue = new LinkedList<>(){{add(root);}};//设置个层数
        int floor = 0;
        while(!queue.isEmpty()){
            ArrayList<Integer> tmp = new ArrayList<>();
            for(int i = queue.size(); i > 0; i--){
                TreeNode node = queue.poll();
                tmp.add(node.val);
                if(node.left != null){
                    queue.add(node.left);
                }
                if(node.right!= null){
                    queue.add(node.right);
                }
            }
            //当奇数层时需要翻转
            if((floor & 1) == 1){
                Collections.reverse(tmp);
            }
            res.add(tmp);
            //层数++
            floor++;
        }
        return res;
    }
}

55-II 平衡二叉树

剑指OFFER思路总结与代码分享——树篇(Java实现)_第11张图片
emmm这题写的有点丑,虽然最后蜜汁跑出了99.96%和100%的成绩…
思路就是递归,感觉不用之前那么一板一眼的分析了,有点感觉了,就直接说我怎么想的吧。首先拿到左边和右边的高度,两个一减取个绝对值,要是这个值大于1呢就说明刚好在你正在处理的root节点完球了,返回false就完事了,不然呢,就返回左子树有没有完球 && 右子树有没有完球。还有一个问题是我们怎么知道左边和右边的高度是多少呢?再写个函数,像“55-1 二叉树的深度”那题那样递归一下就完事了。
所以我写了两个递归咋跑到99.96%的?我看大佬们写的比我简洁多了。


我好像发现了做递归题的idea:
你:算法之神,你只要告诉我前一次_______,我就能给你整出我这个________。
算法之神:小伙子牛逼哦,那我给你前一次__________的结果,你算吧。
你:现在只要把前一次_________xxxxx,然后xxxxx,最后返回的_______就是我这个________了,牛逼不。
算法之神:牛逼哦~


你:算法之神,你只要告诉我前一个节点是不是平衡二叉树,我就能给你整出我这个节点是不是平衡二叉树。
算法之神:小伙子牛逼哦,那我给你前一个节点是不是平衡二叉树的结果,你算吧。
你:现在只要看左边是不是平衡二叉树,再看右边是不是平衡二叉树,要是两边都平衡,那我这个节点就是平衡二叉树,要是有一边不是,那我这个节点就不是平衡二叉树。牛逼不。
算法之神:牛逼哦~


顺便记一下绝对值方法:

Math.abs(i);

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
    	//空树肯定平衡
        if(root == null){
            return true;
        }
        //拿左右子树高度
        int leftNode = treeHeight(root.left);
        int rightNode = treeHeight(root.right);
        //瞅瞅自己这个节点上两个差有没有大于1,大于了就麻溜的false
        if(Math.abs((leftNode - rightNode)) > 1){
            return false;
        }
        //瞅瞅左边,瞅瞅右边
        return isBalanced(root.left) && isBalanced(root.right);
    }

    public int treeHeight(TreeNode root){
   		//空了高度就是0了
        if(root == null){
            return 0;
        }
        //左边高度和右边高度拿到
        int leftHeight = treeHeight(root.left);
        int rightHeight = treeHeight(root.right);
    	//现在的高度就是左右高度高的那嘎达加1
        return Math.max(leftHeight,rightHeight) + 1;
    }
}

28 对称的二叉树

剑指OFFER思路总结与代码分享——树篇(Java实现)_第12张图片
剑指OFFER思路总结与代码分享——树篇(Java实现)_第13张图片
镜像就是左边的左边等于右边的右边,左边的右边等于右边的左边。
我们造一个递归方法,表示该树是否镜像,只要告诉我左边的左边和右边的右边是不是镜像,左边的右边和右边的左边是不是镜像,我就能告诉你当前节点是不是镜像,我只要再判断当前节点的左右是一样的即可。
判断左右节点一样之前要保证他们都不是null或者都是null,不然会报空指针,用这样的模板即可:

//先判断左右是不是都是null
if(left == null && right == null){
	return true;
}
//已经排除左右都是null的情况下,任意一边为null都说明left和right不相等
if(left == null || right == null){
	return false;
}

代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null){
            return true;
        }
        return helper(root.left,root.right);
    }

    private boolean helper(TreeNode left,TreeNode right){
        if(left == null && right == null){
            return true;
        }
        if(left == null || right == null){
            return false;
        }
        //除去null,还有一种false是左右不相等
        if(left.val != right.val){
            return false;
        }
        //算法之神告诉了我左左右右和左右右左是不是镜像
        //如果左右相等,那本节点的结果就是 左左右右 && 左右右左 的结果
        return helper(left.left,right.right) && helper(left.right,right.left);
    }
}

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

剑指OFFER思路总结与代码分享——树篇(Java实现)_第14张图片
这题是传说中的回溯(么?我也不知道),我死在浅拷贝还是深拷贝上了,绝望调试了半天…这地方是个深坑,没踩过十有八九是会忽略的,这题就来唠唠这玩意。
先说整体思路吧,一个List>来接最后的结果,一个List来接(这里插一句,看我顶上一开始申明的LinkedList就知道我想拿栈来做这玩意了,结果发现栈做出来结果是倒序的它不认,加个Collections.reverse()直接给我干掉2ms,拉倒吧还是用队列做吧。),然后就是一个递归函数。
唠唠这个递归函数,首先碰到null返回,其次在本节点中的任务是先把本节点的值加在统计本条线路的值中,然后把本节点塞进队列里,再去查看本节点是不是叶子,如果是叶子的话就看和我们想要的值是否相等,相等就深拷贝一下我们记录的这条线路,然后把本节点的值减掉,把本节点吐出来;要不是叶子节点就向左向右递归,注意递归一路弹回来到这之后还是要把本节点吐出来的,所以不如直接把吐出来放在最后,少打一句话舒坦三十秒。
提一嘴这个函数,用队列的时候可以用上:

LinkedList<Integer> linkedlist = new LinkedList<>();
linkedlist.removeLast();	//删除队列的最后一个元素

先看代码再说坑,代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    List<List<Integer>> res;
    LinkedList<Integer> ways;
    int sum = 0;
    
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        if(root == null){
            return new ArrayList<>();
        }
        //前期准备
        res = new ArrayList<>();
        ways = new LinkedList<>();
        this.sum = sum;
        //调用递归方法
        helper(root,0);
        return res;        
    }

    private void helper(TreeNode root,int waySum){
        if(root == null){
            return;
        }
        //本节点到了,先给他把值加上,塞队里
        waySum += root.val;
        ways.add(root.val);
        //瞅瞅本节点是不是叶子节点啊,是叶子的话总值对不对啊
        if(waySum == sum && root.left == null && root.right == null){
        	//深拷贝的牌面还是要有的,把当前路径深拷贝到结果队列里
            res.add(new LinkedList<>(ways));
        }else{
        	//不是叶子,下面还有节点,药不能停,向左向右继续
            helper(root.left,waySum);
            helper(root.right,waySum);
        }
        //把最后一位移除收尾,对于叶子来说是把叶子移出;
        //对于非叶子来说是递归结束的时候弹到本节点时把本节点移出
        ways.removeLast();
        
    }
}

然后就要说拷贝这事了,我一开始在叶子节点那是这么写的:

if(waySum == sum && root.left == null && root.right == null){
    res.add(ways);
}

然后就麻了,得到的结果是这样的:

[[],[]]

我寻思找出来路径的个数也对了啊,怎么不显示路径呢,我也add(ways)了啊。最后发现我这样写是浅拷贝,拷贝的只是指向ways的一个指针,所以ways一会add(root.val)的一会removeLast()的,我想输出的值也在跟着变了。

浅拷贝(Shallow Copy):
①对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。
②对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

所以这时候要深拷贝一下,直接新建一个LinkedList<>()然后把ways里的值拷贝过去就完事了。

if(waySum == sum && root.left == null && root.right == null){
    res.add(new LinkedList<>(ways));
}

深拷贝(Deep Copy):
相对于浅拷贝而言,对于引用类型的修改,并不会影响到对应的copy对象的值。
每一层的每个对象都进行浅拷贝=深拷贝。

来做个总结:

以后出现结果个数正确、内容错误或消失的时候,可以考虑是否是浅拷贝导致的。

37 序列化二叉树

剑指OFFER思路总结与代码分享——树篇(Java实现)_第15张图片
思路是用BFS做序列化和反序列化,主要还是细心,很多小细节调了不少次,还有就是Sting和Integer的转换注意下,最后注意一下String判相等还是用equals()吧,怎么着都方便点。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root == null){
            return "";
        }
        Queue<TreeNode> queue = new LinkedList<>(){{add(root);}};
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        while(!queue.isEmpty()){
            for(int i = queue.size(); i > 0; --i){
                TreeNode tmp = queue.poll();
                if(tmp != null){
                    sb.append(tmp.val + ",");
                    queue.add(tmp.left);
                    queue.add(tmp.right);
                }else{
                    sb.append("null,");
                }
            }
        }
        sb.append("]"); 
        return sb.toString();
    }

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

}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));

26 树的子结构

剑指OFFER思路总结与代码分享——树篇(Java实现)_第16张图片
首先需要递归的是从本层节点开始,是否有B树的子结构,这个需要一个对比是否是子结构的递归,然后我们还需要一个递归是从本层节点的左右节点开始,是否有B树的子结构,所以这题是双重递归。


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A == null || B == null){
            return false;
        }
        //递归A的节点
        return helper(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);
    }
	
	//递归A某节点向下的树与B树是否一致
    private boolean helper(TreeNode A, TreeNode B){
        if(B == null){
            return true;
        }
        if(A == null){
            return false;
        }
        return A.val == B.val && helper(A.left,B.left) && helper(A.right,B.right);
    }
}

到此为止树的部分就结束啦~求个关注啵啵啵(●´З`●)

你可能感兴趣的:(剑指Offer)