剑指Offer_______树

167 阅读7分钟

7.重建二叉树

**题目描述:**输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如输入前序遍 历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回

解题思路\color{green}{解题思路}:
前序遍历特点:第一个值是根节点 (根左右)
中序遍历特点:根节点左边都是左子树,右边都是右子树(左根右)

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.*;
public class Solution {
    public TreeNode buildTree(int [] pre,int [] in) {
        if(pre==null||in==null||pre.length==0||in.length==0){
            return null;
        }
        
        int index=findindex(pre,in);
        
        //1.获取到树的根节点的value值
        TreeNode root=new TreeNode(pre[0]);
        
        //2.构建left左子树、right右子树
        //root.left=buildTree(左子树的前序数组,左子树的中序数组);
        root.left=buildTree(Arrays.copyOfRange(pre,1,index+1),
        								Arrays.copyOfRange(in,0,index));
                                        
        //root.right=buildTree(右子树的前序数组,右子树的中序数组);
        root.right=buildTree(Arrays.copyOfRange(pre,index+1,pre.length),
        								Arrays.copyOfRange(in,index+1,in.length));
        return root;
    }
    
    //构造一个找根节点下标的函数findindex
    public int findindex(int [] pre,int [] in){
        for(int i=0;i<in.length;i++){
            if(in[i]==pre[0]){
                return i;
            }
        }
        return 0;
    }
}

Arrays.copyOfRange()方法\color{red}{Arrays.copyOfRange()方法} Arrays提供了一个copyOfRange方法进行数组复制
copyOfRange(int[] original, int from, int to)

将一个原始的数组original,从下标from开始复制,复制到上标to,生成一个新的数组。注意这里包括下标from,不包括上标to(Array.copyOfRange的方法是左闭右开的)

8.二叉树的下一个节点

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

解题思路\color{green}{解题思路}:

中序遍历结果{d,b,h,e,i,a,f,c,g}

1.如果当前结点有右子树
下一个节点就是它的右子树的最左子节点(从右子节点出发一致沿着指向左子节点的指针,即可找到) 如:b—>h, a—>f

2.如果当前节点没有右子树
• 如果当前节点是它父节点的左子节点,下一个节点就是它的父节点
如:d—>b, f—>c, h—>e

• 如果当前节点是它父节点的右子节点

沿着指向父节点的指针一直向上遍历,直到找到一个是它父节点的左子节点的节点,如果这样的节点存在,那么这个节点的父节点就是我们要找到的下一个节点
如:i节点的下一个节点:沿着指向父节点的指针向上遍历,先到e,e是b的右子节点,不是,继续向上遍历,到b, b是a的左子节点,因此节点b的父节点a就是i的下一个节点

如:g节点的下一个节点:沿着指向父节点的指针向上遍历,先到c, c是a的右子节点,不是,继续向上遍历到a, a是根节点,无父节点,因此g没有下一个节点

/**
*public class TreeLinkNode {
*    int val;
*    TreeLinkNode left = null;
*    TreeLinkNode right = null;
*    TreeLinkNode next = null; //父节点指针
*
*    TreeLinkNode(int val) {
*        this.val = val;
*    }
* }
**/
public class Solution {
   public TreeLinkNode getNext(TreeLinkNode pNode)
    {
        if (pNode == null) {
        	return null;
        }
        // 右节点不为空
        if (pNode.right != null) {
            pNode = pNode.right;
            
            while (pNode.left != null) {
                pNode = pNode.left;
            }
            return pNode;
        }
        
        // 右节点为空,父节点不为空
        while (pNode.next != null) {
        	// 当前节点是父节点的左孩子则返回父节点
            if(pNode.next.left == pNode) {
            	return pNode.next;
            }
            // 当前节点不是父节点的左孩子,向上找父节点,重复此过程
            pNode = pNode.next;
        }
        // 其他情况返回null
        return null;
    }
}

解题思路\color{green}{解题思路}:主要是分为三种情况:
• 第一种情况就是pNode节点有右孩子时,那么pNode的下一个节点就是右孩子对应的那颗子树的最左侧的节点;
• 如果说当前节点的右孩子为空,并且pNode是pNode父亲节点的左孩子,那么直接返回pNode的父亲节点即可;
• 如果说当前节点的右孩子为空,并且pNode是pNode父亲节点的右孩子那么就返回pNode节点的爷爷节点。

• 当然还有些特殊情况,比如说:二叉树的最右侧节点的判断,以及父亲节点是否为空的判断。

public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode) {
        if (pNode.right != null) {
            // 第一种情况,pNode节点的右孩子不为空
            pNode = pNode.right; //把图中a节点移到c节点
            while (pNode.left != null) { //如果c节点的左节点不为空
                pNode = pNode.left;
            }
            return pNode;
        } else {
            TreeLinkNode tempNode = pNode.next;
            if (tempNode == null) { //情况对应下图,父节点不能为空
                return null;
            }
            if (tempNode.left == pNode) {
                // 第二种情况,当前节点右孩子为空,并且当前节点是父亲节点的左孩子
                return tempNode;
            } else {
                // 第二种情况,当前节点右孩子为空,并且当前节点是父亲节点的右孩子
                boolean flag = false;
                while (tempNode.next != null) {
                    if (tempNode.next.left == tempNode) {
                        flag = true;
                        break;
                    }
                    tempNode = tempNode.next;
                }
                return flag ? tempNode.next : null; // flag尾true时,
                说明pNode所指的节点不是二叉树中最右侧节点(如图中g)
            }
         }
       }
    }

26.树的子结构

**题目描述:**输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构, 即 A中有出现和B相同的结构和节点值 解题思路\color{green}{解题思路}:

/**
 * 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;
        }
        return helper(A,B)||isSubStructure(A.left,B)||isSubStructure(A.right,B);
    }
    public boolean helper(TreeNode A, TreeNode B){
        if(B==null){	//B为null,说明从上往下递归遍历,B已经遍历结束了,而A并没有跳出,说明BA的子结构
            return true;
        }
        if(A==null){//B到这步没有跳出说明不为null,而此时A为null,说明从上往下递归遍历,
        A已经遍历结束了,说明B不是A的子结构
            return  false;
        }
        return A.val==B.val&&helper(A.left,B.left)&&helper(A.right,B.right);
    }
}

27、二叉树的镜像

**题目描述:**请完成一个函数,输入一棵二叉树,该函数输出它的镜像
解题思路\color{green}{解题思路}:
从下往上互换,1-3,6-9再到2-7互换,递归思路为互换2-7时假定1-3,6-9已经互换

/* 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) {	//当前root没有左右子树
           return root;
       }
   	//获取反转过的子树
       TreeNode left=mirrorTree(root.left);	//此时left是子树互换过后的左节点
       TreeNode right=mirrorTree(root.right);	//此时right是子树互换过后的右节点
   	
       //左右互换
       root.right=left;	//把原先右节点换成反转过子树的左节点
       root.left=right;	//把原先左节点换成反转过子树的右节点
       
       return root;
   }
}

使用递归的情况:\color{red}{使用递归的情况:}

28.对称的二叉树

**题目描述:**请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
解题思路\color{green}{解题思路}:
本题:

/**
 * 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);
    }
    public boolean helper(TreeNode A, TreeNode B){
    	//边界条件,递归出口
        if(A==null&&B==null){return true;}
        if(A==null||B==null){return false;}
        return A.val==B.val && helper(A.left,B.right) && helper(A.right,B.left);
    }  
}

32.从上到下打印二叉树

题目描述:A\color{green}{A}从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
解题思路\color{green}{解题思路}:

/**
 * 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[]{};}	//如果root为空,返回一个空数组

        //两种数据结构 Queue + List,前者用来先进先出,后者用来接收结果
        Queue<TreeNode> queue=new LinkedList<>(); 
        ArrayList<Integer> list =new ArrayList<>();
        
        //首先将根节点加入队列中
        queue.add(root);
        
        //核心方案:queue中取出一个元素放入list,再把其左右孩子放入queue
        while(!queue.isEmpty()){
            TreeNode node=queue.remove();	//queue中取出一个节点
            list.add(node.val);  //把节点值放入list中
            
            //左右孩子不空,放入queue中
            if(node.left!=null){ queue.add(node.left);}
            if(node.right!=null){ queue.add(node.right);}
        }
        //题目要求返回int[]数组,于是把ArrayList类型转换为int[]数组
        int[] res=new int[list.size()];
        for(int i=0;i<list.size();i++){
            res[i]=list.get(i);
        }
        return res;
    }
}

LinkedList\color{green}{LinkedList类}实现了Queue接口,因此我们可以把LinkedList当成Queue来用
Queue的常用方法:
add()\color{red}{add( )}  将指定的元素插入此队列
remove()\color{red}{remove( )}  获取并移除此队列的头
List的常用方法:
add(element)\color{red}{add(element)}: 向列表的尾部添加指定的元素
size()\color{red}{size()}: 返回列表中的元素个数
get(index)\color{red}{get(index)}: 返回列表中指定位置的元素,index从0开始

题目描述:B\color{green}{B}从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
解题思路\color{green}{解题思路}:

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res=new ArrayList<>();
        if(root==null){return res;}	//如果root为空,返回一个空的res
 
        Queue<TreeNode> queue=new LinkedList<>(); 
        
        //首先将根节点加入队列中
        queue.add(root);
        
        while(!queue.isEmpty()){
            ArrayList<Integer> temp=new ArrayList<>();	
            int size=queue.size();	//遍历某一层,先获取该层的size,先获得queue.size(),防止fast fail
            
            for(int i=0;i<size;i++){	//每个for循环完成一层的遍历。不能写i<queue.size;因为size是动态数值
            
            	TreeNode node=queue.remove();	//queue中取出一个节点
            	temp.add(node.val);  //把一层所有的值加入temp中
            
            	//左右孩子不空,放入queue中
            	if(node.left!=null){ queue.add(node.left);}
            	if(node.right!=null){ queue.add(node.right);}
            }
            res.add(temp);	//把每一层结果放到res中
        }
        return res;
    }
}

queue.size()\color{red}{queue.size() }返回队列中元素的个数(二叉树的一层为一个queue)

题目描述:C\color{green}{C}请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。 解题思路\color{green}{解题思路}:

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res=new ArrayList<>();
        if(root==null){return res;}	//如果root为空,返回一个空的res
 
        Queue<TreeNode> queue=new LinkedList<>(); 
        
        queue.add(root);	//首先将根节点加入队列中

        boolean flag=true;	//首次默认尾部添加,true

        while(!queue.isEmpty()){
            List<Integer> temp =new LinkedList<>();  //LinkedList添加元素更快
            //ArrayList<Integer> temp=new ArrayList<>();
            
            int size=queue.size();	//遍历某一层,先获取该层的size,先获得queue.size(),防止fast fail
            
            for(int i=0;i<size;i++){	//每个for循环完成一层的遍历
            	TreeNode node=queue.remove();	//queue中取出一个节点
            	
                if(flag){
                temp.add(node.val);  //奇数行,List中尾部添加
                }  
                else{
                    temp.add(0,node.val);  //偶数行,List中头部插入
                }
            	//左右孩子不空,放入queue中
            	if(node.left!=null){ queue.add(node.left);}
            	if(node.right!=null){ queue.add(node.right);}
            }
            flag=!flag;  //使得flag循环一次为true,一次为false
            res.add(temp);	//把每一层结果放到res中
        }
        return res;
    }
}

List的常用方法:
add(Object element): 向列表的尾部添加指定的元素。
add(int index, Object element): 在列表的指定位置插入指定元素

ArrayList底层是数组,头部插入会使得所有元素向后复制一遍;
而LinkedList底层是双向链表,头部插入只需加一个指针和节点即可

总结与思考\color{green}{总结与思考}:

32.A\color{green}{A}二叉树的深度

**题目描述:**输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

解题思路\color{green}{解题思路}:
一棵树的深度可以认为是它孩子节点深度+1

/**
 * 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;}	//一个节点都没有,深度当然为0
        
        //分别得到了root根节点左右孩子的深度
        int l=maxDepth(root.left);
        int r=maxDepth(root.right);
        
        if(l>r){
            return l+1;
        }else{
            return r+1;
        }
        //return Math.max(l+1,r+1);
    }
}
class Solution {
    public int maxDepth(TreeNode root) {
        return root==null?0:Math.max(maxDepth(root.left),maxDepth(root.right))+1;
    }
}

B\color{green}{B}平衡二叉树

**题目描述:**给定一个二叉树,判断它是否是高度平衡的二叉树。 本题中,一棵高度平衡二叉树定义为: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
解题思路\color{green}{解题思路}: 不仅要考虑根节点的左右高度差的绝对值是否超过1,还要考虑其左右子树的高度差的绝对值是否超过1

/**
 * 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 l=maxDepth(root.left);
        int r=maxDepth(root.right);
        //|l-r|<=1
        return (l-r>=-1 && l-r<=1)&& isBalanced(root.left) && isBalanced(root.right);
    }

	public int maxDepth(TreeNode root) {
        if(root==null){return 0;}	//一个节点都没有,深度当然为0
        
        //分别得到了root根节点左右孩子的深度
        int l=maxDepth(root.left);
        int r=maxDepth(root.right);
        
        if(l>r){
            return l+1;
        }else{
            return r+1;
        }
        //return Math.max(l+1,r+1);
    }
}