剑指offer :树类题目汇总

7.重建二叉树:

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

       解题思路:把重建二叉树分解为构建左右子树。根据题目给出的前序遍历、中序遍历数组,首先找出根节点 , 然后再根据中序遍历找到左子树和右子树的长度,分别构造出左右子树的前序遍历和中序遍历序列 ,最后分别对左右子树采取递归。

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre==null || in==null || pre.length !=in.length)//特殊情况
            return null;
        else
            return reTree(pre,0,pre.length-1,in,0,in.length-1);
    }
    public TreeNode reTree(int [] pre,int stpre,int endpre,int [] in,int stin,int endin){
        if(stpre>endpre || stin>endin)
            return null;
        int top = pre[stpre];
        TreeNode root= new TreeNode(pre[stpre]);
        for(int i=stin;i<=endin;i++){
            if(in[i]== top){
                root.left = reTree(pre,stpre+1,stpre-stin+i,in,stin,i-1);
                root.right = reTree(pre,stpre-stin+i+1,endpre,in,i+1,endin);
            }
        }
        return root;
    }
}


 

8.二叉树的下一个结点:

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

        解题思路:二叉树的下一个节点,一共有以下情况:1.二叉树为空,则返回空;2.节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点;3.节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。

/*
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;
            else
                pNode = pNode.next;
        }
        return null;        
    }
}

 

32.从上往下打印二叉树:

             从上往下打印出二叉树的每个节点,同层节点从左至右打印。

            解题思路:从上到下打印二叉树的规律:每次打印一个结点时,将该结点的子节点放入到队列的末尾,从队首取出元素进行下一次判断,直至队列为空。(队列的类型可以为TreeNode,方便后续操作,同时,当二叉树根结点为空时,输出为空---注意位置!!!,放在开头会出现返回为[],而不是{})

import java.util.ArrayList;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
import java.util.*;
public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        if(root == null)
            return result;
        queue.add(root);
        while(queue.size()!=0){
            TreeNode temp = queue.poll();
            if(temp.left!=null )
                 queue.add(temp.left);
            if(temp.right!=null )
                 queue.add(temp.right);
            result.add(temp.val);
        }
        return result;
    }
}

           从上到下按层遍历二叉树,从本质上讲就是广度优先遍历二叉树。将起始结点放入到队列中,接下来,每次从队列的头部取出一个结点,遍历这个结点的子结点,依次放入到队列中,重复遍历直至所有结点都被遍历为止。

---->把二叉树打印成多行:

          从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

           解题思路:按照上一题中从上到下打印二叉树的思路,在此基础上添加两个变量,用来记录下一行需要打印的个数,和该行未打印个数。特别注意的是,方法的返回类型要求,需要重新定义一个ArrayList,并在每次添加到result后清空原有数据。

import java.util.ArrayList;
import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        if(pRoot == null)
             return result;
        int next = 0;//下一层要打印的个数
        int remains = 1;//该层还没有打印的个数
        queue.add(pRoot);
        while(queue.size()!=0){
            TreeNode temp = queue.poll();
            if(temp.left!=null ){
                 queue.add(temp.left);
                 next++;
            }
            if(temp.right!=null ){
                 queue.add(temp.right);
                 next++;
            }
            list.add(temp.val);
            remains--;
            if(remains==0){
                remains = next;
                next = 0;
                result.add(list);
                list = new ArrayList<Integer>();
            }
        }
        return result;
    }
    
}

---->按之字形打印二叉树:

           请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

           解题思路:按之字形打印二叉树,需要两个栈,当打印层是奇数层时,将子节点按照从左到右的顺序保存到第一个栈中,当打印层是偶数层时,将子节点按照从右到左的顺序保存到第一个栈中,当某一层的数据打印完成后,将两个栈交换,并继续打印下一层。

import java.util.ArrayList;
import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> list = new ArrayList<Integer>();
        Stack<TreeNode> s1 = new Stack<TreeNode>();//存放偶数层
        Stack<TreeNode> s2 = new Stack<TreeNode>();//存放奇数层
        int next=0,remains = 1,level=1;//下一层,这一层剩余结点,层数
        if( pRoot == null)
            return result;
        s1.push(pRoot);
        while(s1.size()!=0 || s2.size()!=0){
           if(level%2!=0){
             if(s1.size()!=0){
                 TreeNode temp = s1.pop();
                 if(temp.left!=null){
                     s2.push(temp.left);
                     next++;
                 }
                 if(temp.right!=null){
                     s2.push(temp.right);
                     next++;
                 }
                  list.add(temp.val);
                  remains--;
               }
        }else{
            if(s2.size()!=0){
                 TreeNode temp = s2.pop();
                if(temp.right!=null){
                     s1.push(temp.right);
                     next++;
                 }
                  if(temp.left!=null){
                     s1.push(temp.left);
                     next++;
                 }
                 list.add(temp.val);
                 remains--;
            }
        }
         if(remains==0){
                remains = next;
                next=0;
                level++;
                result.add(list);
                list = new ArrayList<Integer>();
            }
        }
     return result;
    }
}

 

55.二叉树的深度:

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

           解题思路:采用递归的方式实现,树的深度为左右子树中深度较大的值+1。

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root==null)
            return 0;
        int ldeepth = TreeDepth(root.left);
        int rdeepth = TreeDepth(root.right);
        if(ldeepth>=rdeepth)
            return ldeepth+1;
        else 
            return rdeepth+1;
    }
}

---->平衡二叉树:

          输入一棵二叉树,判断该二叉树是否是平衡二叉树。如果某二叉树中任意结点的左右子树的深度相差不超过1,即为平衡二叉树。

         解题思路:遍历每个结点时,调用上一题中求解二叉树深度的方法,判断每个结点的深度差。

public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root==null)
            return 0;
        int ldeepth = TreeDepth(root.left);
        int rdeepth = TreeDepth(root.right);
        if(ldeepth>=rdeepth)
            return ldeepth+1;
        else 
            return rdeepth+1;
    }
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root==null)
            return true;
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        int dif = left-right;
        if(dif>1 || dif<-1)
            return false;
        return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
    }
}

 

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

          输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

         解题思路:二叉搜索树的左子树总比根结点小,右子树比根结点大。采用递归方式。Arrays.copyOfRange(T[ ] original,int from,int to),将一个原始的数组original,从小标from开始复制,复制到小标to,生成一个新的数组,包括下标from,不包括下标to。

import java.util.Arrays;
public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence==null||sequence.length==0)
            return false;
        int root = sequence[sequence.length-1];
        int i=0;
        for(;i<sequence.length-1;i++){
            if(sequence[i]>root)
                break;
        }
        int j = i;
        for(;j<sequence.length-1;j++){
            if(sequence[j]<root)
                return false;
        }
        boolean resultl =true;
        boolean resultr = true;
        if(i > 0)
            resultl = VerifySquenceOfBST(Arrays.copyOfRange(sequence,0, i));
        if(i <sequence.length-1)
            resultr =  VerifySquenceOfBST(Arrays.copyOfRange(sequence,i, sequence.length-1));
        return (resultl && resultr) ;         
    }
}

 

54.二叉搜索树的第K个结点:

           给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。

          解题思路:利用中序遍历算法即可。

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    //中序遍历
    int index = 0;
    TreeNode KthNode(TreeNode pRoot, int k){
        TreeNode target  = null;
        if(pRoot!=null){
           if(pRoot.left!=null)
               target = KthNode(pRoot.left,k);
           if(target!=null)
               return target;
            //左子树中没有
            index++;
            if(index==k)
                return pRoot;
            target = KthNode(pRoot.right,k);
            if(target!=null)
                return target;
           }
        return null;
    }
}

 

37.序列化二叉树:

        请实现两个函数,分别用来序列化和反序列化二叉树。

        解题思路:前序遍历是从根结点开始,可以用来序列化二叉树。序列化与反序列化的过程采用递归的方式。把二叉树分为根结点、左子树、右子树。

        对于序列化:使用前序遍历,递归的将二叉树的值转化为字符,并且在每次二叉树的结点不为空时,在转化val所得的字符之后添加一个' , '作为分割。对于空节点则以 '%' 代替。

       对于反序列化:按照前序顺序,递归的使用字符串中的字符创建一个二叉树,递归函数的参数为string ,通过索引的变化保证字符串的逐渐移动。

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/

public class Solution {
    int index=-1;
    String Serialize(TreeNode root) {
        StringBuilder str = new StringBuilder();
        Serialize2(root , str);
        return str.toString();
    }
        public void Serialize2(TreeNode root , StringBuilder str){
            if(root == null)
                str.append("%,");
            else{
                str.append(root.val + ",");
                Serialize2(root.left , str);
                Serialize2(root.right , str);
            }
        }
    
    TreeNode Deserialize(String str) {
        if(str =="" )
            return null;
        String[] ser = str.split(",");
        return Deserialize(ser);
    }
        
     public TreeNode Deserialize(String[] str) {
         index++;
         while(!str[index].equals("%")){
             TreeNode root = new TreeNode(Integer.parseInt(str[index]));
             root.left = Deserialize(str);
             root.right = Deserialize(str);
             return root;
              }
        return null;
      }
}

 

27.二叉树的镜像:

        操作给定的二叉树,将其变换为源二叉树的镜像。

       解题思路:二叉树的镜像即把每个结点的左右结点交换,采用递归方式。

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public void Mirror(TreeNode root) {
        if(root == null)
            return;
        if(root.left == null && root.right == null)
            return ;
        TreeNode temp = root.right;
        root.right = root.left;
        root.left = temp;
        if(root.left!=null)
            Mirror(root.left);
        if(root.right!=null)
            Mirror(root.right);
    }
}

 

28.对称的二叉树:

          请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

          解题思路:对特殊情况进行处理后,按照左子树的左子树和右子树的右子树相同,左子树的右子树和右子树的左子树相同即可,采用递归。

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    boolean isSymmetrical(TreeNode pRoot){
        return Symmetrical(pRoot,pRoot);
    }
     
     boolean Symmetrical(TreeNode root1,TreeNode root2){
       if(root1 == null && root2==null)
            return true;
       if(root1 == null || root2==null)
            return false;
       if(root1.val != root2.val)
            return false;
        return Symmetrical(root1.left,root2.right) 
                    && Symmetrical(root1.right,root2.left); 
    }
}

 

26.树的子结构:

          输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

         解题思路:采用递归的方式,先找到树A中与树B根结点相同的结点,然后开始判断树A树B 的左右子树是否相同。注意空指针的特殊情况。

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
     public boolean IsSubtree(TreeNode root1,TreeNode root2) {
        if(root1==null && root2!=null )
            return false;
        if(root2==null)
            return true;
        if(root1.val != root2.val)
            return false;
        return IsSubtree(root1.left,root2.left) && IsSubtree(root1.right,root2.right);
    }
    
     public boolean HasSubtree(TreeNode root1,TreeNode root2){
            boolean result = false;
            if(root1 != null && root2 != null){
                if(root1.val == root2.val){
                    result = IsSubtree(root1,root2);
                }
                if(!result){result = HasSubtree(root1.left, root2);}
                if(!result){result = HasSubtree(root1.right, root2);}
            }
            return result;        
        } 
}

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值