[剑指offer]大可日常打卡-树

8.二叉树的下一个节点

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

思路:中序遍历,左根右,

    如果一个节点右孩子不为空的话,那么返回他的右子树的最左节点;

    如果右孩子为空,可能有两种情况,(1)该节点是父节点的左节点,那么返回父节点即可,(2)该节点是父节点的右节点,那么把p指向它的父节点,一直往上指,直到p是p父节点的左孩子,返回p的父节点,p的父节点变成null了,说明该节点是最后一个节点,返回null。

    思路正确,第一次提交代码如下,但是超时了。

/*
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 pNode;
        }
        if(pNode.right!=null){
            pNode=pNode.right;
            while(pNode.left!=null){
                pNode=pNode.left;
            }
            return pNode;
        }
        
        
        if(pNode==pNode.next.left){
            return pNode.next;
        }else{
            TreeLinkNode p=pNode;
            while(p.next!=null){
                p=pNode.next;
                if(p==p.next.left){
                    return p;
                }
            }
            return null;
        }
    }
}

    调整完以后,如下,第二个过程之前写的太复杂了!所以超时!

/*
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==pNode.next.left){
                return pNode.next; 
            }
            pNode=pNode.next;
            
        }
        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.left;
        root.left=root.right;
        root.right=temp;
        
        if(root.left!=null){
            Mirror(root.left);
        }
        if(root.right!=null){
            Mirror(root.right);
        }
    }
}

7.重建二叉树

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

思路:感觉也是要用递归了。加油77!多看一个题就多会一个,还有那么多要看呢。不要再胡思乱想啦!

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.Arrays;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre==null||in==null||pre.length==0||in.length==0){
            return null;
        }
        TreeNode root=new TreeNode(pre[0]);
        for(int i=0;i<in.length;i++){
            if(in[i]==pre[0]){ //找到了root节点
                root.left=reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));
                root.right=reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length));
            }
        }
        return root;
    }
}

37.序列化二叉树

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

思路:反序列化稍微复杂一丢丢。先维护一个整型变量index=-1,表示str的当前索引。

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

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    String Serialize(TreeNode root) {
        if(root==null){
            return "#,";
        }
        StringBuilder sb=new StringBuilder();
        sb.append(root.val+",");
        sb.append(Serialize(root.left));
        sb.append(Serialize(root.right));
        return sb.toString();
        
    }
    int index=-1;
    TreeNode Deserialize(String str) {
        index++;
        if(index>=str.length()){
            return null;
        }
        String[] values=str.split(",");
        TreeNode root=null;
        if(!values[index].equals("#")){
            root=new TreeNode(Integer.valueOf(values[index]));
            root.left=Deserialize(str);
            root.right=Deserialize(str);
            
        }
        return root;
    }
}

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 isSymmetrical(pRoot,pRoot);
    }
    boolean isSymmetrical(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 isSymmetrical(root1.left,root2.right)&&isSymmetrical(root1.right,root2.left);
    }
    
}

26.树的子结构

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

思路:首先遍历树A找到与B根节点一致的节点,然后再看B是不是包含于A,所以分为两步!

对于A中的每一个节点,递归调用HasSubtree,递归出口是某一个节点的val和A的根节点的val一致,然后对这两个节点开始调用DoesRoot1HasRoot2;

在DoesRoot1HasRoot2的调用过程中,递归出口是root2==null,表示root2已经遍历完,遍历完说明是包含的,都走到这一步!如果root2还没有遍历完root1就走完了,说明不包含,如果左右子节点有一个不相同那么也说明不包含,直接返回false,不往下走了。

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

    public TreeNode(int val) {
        this.val = val;

    }

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

32.从上到下打印二叉树

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

思路:把节点存到一个ArrayList<Integer>里面。77加油!上个厕所继续想!也是考查二叉树遍历算法,只不过不是我们熟悉的前序、中序、后序遍历,是按层遍历,可以先通过分析一个例子来找到思路。

    用一个队列,当打印根节点以后,就把左孩子和右孩子都放入队列中,然后从队列取一个数,再把它的左孩子右孩子放入队列中,直到队列为空,就打印完毕。

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.Queue;
import java.util.ArrayList;
import java.util.LinkedList;
public class Solution {
    
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> res=new ArrayList<Integer>();
        if(root==null){
            return res;
        }
        
        Queue<TreeNode> queue=new LinkedList<>();
        //res.add(root.val);
        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode node=queue.poll();
            res.add(node.val);
            if(node.left!=null){
                queue.add(node.left);
            }
            if(node.right!=null){
                queue.add(node.right);
            }
        }
        return res;
    }
}

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

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

思路:写一个实例进行分析一下。因为是后序遍历,所以最后一个数是根。首先思考一个整数数组怎么样才不是一棵二叉搜索树的后序遍历结果,所以要想二叉搜索树的后序遍历结果有什么特征!!!不满足这个特征的就不是!!!

    首先二叉搜索树的特征是什么,左孩子小于根节点,右孩子大于根节点。

import java.util.Arrays;
public class Solution {
    public static boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence==null||sequence.length==0){
            return false;
        }
        int len=sequence.length;
        int root=sequence[len-1]; //根节点
        int i;
        for(i=0;i<len-1;i++){
            if(sequence[i]>root){ //右子树的开始部分
                
                break;
            }
        }
        //System.out.println(startr);
        for(int j=i;j<len-1;j++){  //右边的要是有小于root的,那么就返回false
            if(sequence[j]<root){
                return false;
            }
        }
        boolean left=true;
        if(i>0){
            left=VerifySquenceOfBST(Arrays.copyOfRange(sequence,0,i));
        }
        boolean right=true;
        if(i<len-1){
            right=VerifySquenceOfBST(Arrays.copyOfRange(sequence,i,len-1));
        }
        
        return left&&right;
    }
}

最需要注意的是,在寻找右子树的第一个节点位置的时候,必须要和i一致,因为有可能只有左子树啊!!

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

    输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

思路:寻找思路。因为路径定义为从根节点开始往下,树一共有三种遍历方式,前序、中序和后序,只有前序是先访问根节点的!当用前序遍历的方式访问到某一结点时,我们把该节点加入到路径上,并累加该节点的值。如果该节点为叶节点,并且路径中的和刚好等于输入的整数,那么当前路径符合要求。如果当前节点不是叶节点,那么继续访问它的子节点。当前结点访问结束后,递归函数自动回到它的父节点。因此,我们在函数退出之前要在路径上删除当前节点并减去当前节点的值,以确保返回父节点时路径刚好是从根节点到父节点。

import java.util.ArrayList;
/**
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>> pathlist=new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> path=new ArrayList<Integer>(); //全局变量
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        if(root==null){
            return pathlist; 
        }
        path.add(root.val);
        target-=root.val;
        if(target==0&&root.left==null&&root.right==null){
            pathlist.add(new ArrayList<Integer>(path));
        }
        FindPath(root.left,target);
        FindPath(root.right,target);
        path.remove(path.size()-1); //完了以后要删除最后一个
        return pathlist;
    }
}

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

    输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

思路:举个例子寻找思路。七七加油~左孩子的最右节点指向根节点,根节点指向右孩子的最左节点~是一个左根右遍历的过程。

(1)先把左子树变成双向链表,返回left;

(2)到左子树双向链表的最后一个;

(3)接起来;

(4)把右子树变成双向链表;

(5)右子树和root之前的接起来;

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

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
import java.util.LinkedList;
public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
        LinkedList<Integer> list=new LinkedList();
        if(pRootOfTree==null){
            return pRootOfTree;
        }
        if(pRootOfTree.left==null&&pRootOfTree.right==null){
            return pRootOfTree;
        }
        TreeNode left=Convert(pRootOfTree.left);
        TreeNode p=left;
        while(p!=null&&p.right!=null){
            p=p.right;
        }
        if(left!=null){
            p.right=pRootOfTree;
            pRootOfTree.left=p;
        }
        
        
        TreeNode right=Convert(pRootOfTree.right);
        TreeNode q=right;
        if(q!=null){
            pRootOfTree.right=q;
            q.left=pRootOfTree;
        }
        if(left!=null){
            return left;
        }
        return pRootOfTree;
        //return left;
    }
}

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

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

思路:写一个例子寻找思路,二叉搜索树按照中序遍历的顺序打印出来正好就是排好序的顺序,左中右,所以就使用一个计数器,从1开始,每遍历一个数,就计数器+1,直到计数器=k,那么当前遍历到的节点就是二叉搜索树的第k大节点。

/*
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)
    {
        if(pRoot==null||k<1){
            return null;
        }
        TreeNode node=KthNode(pRoot.left,k);
        if(node!=null){
            return node;
        }
        index++;
        if(index==k){
            return pRoot;
        }
        node=KthNode(pRoot.right,k);
        if(node!=null){
            return node;
        }
        return null;
    }
}

55.二叉树的深度

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

思路:维护一个动态的max。

/**
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) {
        int max=0;
        //int depth=0;
        if(root==null){
            return 0;
        }
        int left=0;
        if(root.left!=null){
            left=TreeDepth(root.left);
        }
        int right=0;
        if(root.right!=null){
            right=TreeDepth(root.right);
        }
        max=Math.max(left+1,right+1);
        return max;
        
    }
}

牛客上关于树的剑指offer专版多的两道题:

1.按之字形顺序打印二叉树

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

思路:这个题七七你之前写过哦~~写不出来是不是太不厉害了~

    先找一个例子进行分析,不要一看到这种需要稍微思考一下的题就被它吓住了。首先肯定是二叉树按层遍历吧,然后把每一层的都存到一个ArrayList里面,然后对于某一层的,reverse一下。

    二叉树的按层遍历还记得吗,有一个队列,遍历过的节点放到队列里面,然后取出一个就把它的左孩子和右孩子压进去,直到队列为空,遍历完毕。

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.LinkedList;
import java.util.Iterator;
public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer> > res=new ArrayList<>();
        if(pRoot==null){
            return res;
        }
        ArrayList<Integer> arr=new ArrayList<>();
        LinkedList<TreeNode> queue=new LinkedList<>();
        queue.addLast(null); //层分隔符
        queue.addLast(pRoot); 
        boolean leftToright=true;
        while(queue.size()!=1){
            TreeNode node=queue.removeFirst();
            if(node==null){ //如果到达了层分隔符,也就是说一层打印完毕了~
                Iterator<TreeNode> iter=null; //
                if(leftToright){
                    iter=queue.iterator();
                }else{
                    iter=queue.descendingIterator();//降序
                }
                leftToright=!leftToright; //改为相反的
                while(iter.hasNext()){
                    TreeNode tmp=iter.next();
                    arr.add(tmp.val);
                }
                res.add(new ArrayList<Integer>(arr));
                arr.clear();
                queue.add(null);
                continue;
            }
            if(node.left!=null){
                queue.addLast(node.left);
            }
            if(node.right!=null){
                queue.addLast(node.right);
            }
        }
        return res;

    }

}

2.把二叉树打印成多行

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

思路:思路同上。

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.LinkedList;
import java.util.Iterator;
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res=new ArrayList<>();
        if(pRoot==null){
            return res;
        }
        ArrayList<Integer> list=new ArrayList<>();
        LinkedList<TreeNode> queue=new LinkedList<>();
        queue.addLast(null);
        queue.addLast(pRoot);
        while(queue.size()!=1){
            TreeNode node=queue.removeFirst();
            Iterator<TreeNode> iter=null;
            if(node==null){
                iter=queue.iterator();
                while(iter.hasNext()){
                TreeNode tmp=iter.next();
                list.add(tmp.val);
                }
                res.add(new ArrayList(list));
                list.clear();
                queue.add(null);
                continue;
            }
            if(node.left!=null){
                queue.addLast(node.left);
            }
            if(node.right!=null){
                queue.addLast(node.right);
            }
            
            
        }
        return res;
    }
    
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值