《剑指offer》------树专题


重建二叉树

我的想法:

 * 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;
        return  reConstructBinaryTree(pre,0,pre.length,in,0,in.length-1);
    }
    
    public TreeNode reConstructBinaryTree(int [] pre, int start1,int end1,int[] in,int start2,int end2){
        if(start1>end1 || start2>end2)                           //判断输入
            return null;
        
        int rootData=pre[start1];                                //根节点数值
        TreeNode head=new TreeNode(rootData);                   //创建根节点
        int rootIndex=findRootIndexOfIn(in,rootData,start2,end2);               //找到中序中根节点所在位置序号
        int offset=rootIndex-1-start2;                                          //左子树的偏移量(大小)
        //确定根节点在两个序列中的位置之后 分别对左右子树进行递归(同样操作)
        TreeNode left=reConstructBinaryTree(pre,start1+1, start1+1+offset, in, start2,start2+offset);
        TreeNode right=reConstructBinaryTree(pre, start1+offset+2,end1, in, rootIndex+1,end2);
        head.left=left;
        head.right=right;
        return head;                                         //返回根节点
    }
    
    /*找到根节点在中序所在位置*/
    public int findRootIndexOfIn(int[] in,int x,int start,int end){
        if(in==null || start>end)
            return -1;
        for(int i=start;i<=end;i++){
            if(in[i]==x)
                return i;
        }
        return  -1;
    }
}

最简思路:

public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre==null || in==null || pre.length!=in.length)  //对输入条件进行判断
            return null;
        return  reConstructBinaryTree(pre,0,pre.length,in,0,in.length-1);
    }
     public TreeNode reConstructBinaryTree(int [] pre, int start1,int end1,int[] in,int start2,int end2){
         if(start1>end1 || start2>end2)
             return null;
         
         TreeNode root=new TreeNode(pre[start1]);   
         for(int i=start2;i<=end2;i++){
             if(in[i]==pre[start1]){
                 //int offset=i-1-start2;    //偏移量  
                 root.left=reConstructBinaryTree(pre,start1+1,start1+i-start2, in,start2,i-1);
                 root.right=reConstructBinaryTree(pre,start1+i-start2+1,end1,in,i+1,end2);
                 break;                    //找到根节点就需要退出循环
             }
         }
         
         return root;
     }





树的子结构






public class Solution {
    public static boolean HasSubtree(TreeNode root1, TreeNode root2) {
        boolean result = false;
        //当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
        if (root2 != null && root1 != null) {
            //如果找到了对应Tree2的根节点的点
            if(root1.val == root2.val){
                //以这个根节点为为起点判断是否包含Tree2
                result = doesTree1HaveTree2(root1,root2);
            }
            //如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
            if (!result) {
                result = HasSubtree(root1.left,root2);
            }
             
            //如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
            if (!result) {
                result = HasSubtree(root1.right,root2);
               }
            }
            //返回结果
        return result;
    }
 
    public static boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
        //如果Tree2已经遍历完了都能对应的上,返回true
        if (node2 == null) {
            return true;
        }
        //如果Tree2还没有遍历完,Tree1却遍历完了。返回false
        if (node1 == null) {
            return false;
        }
        //如果其中有一个点没有对应上,返回false
        if (node1.val != node2.val) {  
                return false;
        }
         
        //如果根节点对应的上,那么就分别去子节点里面匹配
        return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right);
    }




二叉树的镜像





方法1:递归

public void Mirror(TreeNode root) {
        if(root==null ||(root.left==null && root.right==null))   //空节点和叶子节点无需交换 直接返回  作为递归的结束条件
            return ;
        TreeNode tmp=root.left;    //交换根节点的左右子节点,只要不是两个节点都为空即可
        root.left=root.right;
        root.right=tmp;
        
        if(root.left!=null){        //若根节点存在左节点 则继续以此节点作递归
            Mirror(root.left);
        }
        if(root.right!=null){
            Mirror(root.right);
        }
    }
方法2:非递归(循环)用到栈
public void Mirror(TreeNode root) {
        if(root==null)
            return;
        Stack<TreeNode> s=new Stack<TreeNode>();
        s.push(root);                   //压入根节点入栈
        while(!s.empty()){
            TreeNode tree=s.pop();      //弹出根节点
            if(tree.left!=null || tree.right!=null){    //假如有子节点---则交换
                TreeNode temp=tree.left;
                tree.left=tree.right;
                tree.right=temp;
            } 
            if(tree.left!=null)               //若左节点不为空 则继续压入
                s.push(tree.left);
            if(tree.right!=null)
                s.push(tree.right);
        }
    }





从上往下打印二叉树




关键:模拟队列

public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
       
        ArrayList<Integer>  al=new ArrayList<Integer> (); 
        ArrayList<TreeNode>  queue=new ArrayList<TreeNode> ();  //模拟队列
        if(root==null)                                         //若根节点为空  返回list为空  
            return al;
        queue.add(root);
        while(queue.size()!=0){
            root=queue.remove(0);
            al.add(root.val);
            if(root.left!=null){
                queue.add(root.left);
            }
            if(root.right!=null){
                queue.add(root.right);
            }
        }
        return al;

    }
}






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





import java.util.Arrays;
public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence==null || sequence.length<=0)
            return false;
        int length=sequence.length;
        int root=sequence[length-1];       //最右边元素一定为根节点的值
        int i=0;                           //找左右子树的分界点
        for(;i<length-1;i++){
            if(sequence[i]>root)           //循环数组 若有比根节点值大的元素 则认为是分界点
                break;
        }
        int j=i;
        for( ;j<length-1;j++){             //在右子树中有比根节点值小的 则不符合要求
            if(sequence[j]< root)
                return false;
        }
        
        //到此为止  证明第一轮左右子树均符合要求 之后继续递归 分别判断左右子树
        boolean left=true;
        if(i>0)   //分界点>0 证明有左子树
            left=VerifySquenceOfBST(Arrays.copyOfRange(sequence,0,i));//不包含i
        boolean right=true;
        if(i<length-1)  //分界点<length-1  证明有右子树
            right=VerifySquenceOfBST(Arrays.copyOfRange(sequence,i,length-1));
        
        return right && left ;
        }
}





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



方法1: 借助于ArrayList结构(牛客网上参考)

public class Solution {
    private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
    private ArrayList<Integer> list = new ArrayList<Integer>();
    
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        if(root == null)
            return listAll;
        list.add(root.val);  
        target -= root.val;
        //叶子节点且值同 则加路径 
        if(target == 0 && root.left == null && root.right == null)
            listAll.add(new ArrayList<Integer>(list));
        //没有到达叶子节点则  继续遍历左右节点
        FindPath(root.left, target);
        FindPath(root.right, target);
        //回退时 去掉该节点 回到父节点
        list.remove(list.size()-1);   target+=root.val; 
        return listAll;
    }
}

方法2 :用Stack 结构实现(我的想法)

public class Solution {
    public ArrayList<ArrayList<Integer>>  FindPath(TreeNode root,int target)  {
        ArrayList<ArrayList<Integer>> al=new  ArrayList<ArrayList<Integer>>();
        if(root==null || target<=0)
            return al;
        
        Stack<Integer> s=new Stack<Integer>();  
        int curSum=0;          //现有求和
        
        findPath(root,target,s,curSum,al);
        return al;
    }
    
    public ArrayList<ArrayList<Integer>> findPath(TreeNode root,int target,Stack<Integer> s,int curSum,ArrayList<ArrayList<Integer>> al){
        ArrayList<Integer>  all=new ArrayList<Integer>();
        s.push(root.val);       //压入根节点值
        curSum+=root.val;       //现有求和+根节点值
        //已经到达叶子节点  则判断和是否满足给定值   若满足 则记录路径  若不满足  则回退至父节点
        if(root.left==null && root.right==null){   
            if(curSum==target){                    //若满足条件
                for(int i:s ){
                    all.add(i);
                }
                al.add(all);          //一条路径存入al中
            }
        }
        
        //未到达叶子节点 则继续遍历节点
        if(root.left!=null){
            findPath(root.left,target,s,curSum,al);
        }
        if(root.right!=null){
            findPath(root.right,target,s,curSum,al);
        }
        
        //返回到调用函数前  需要回退
        root.val=s.pop();
        curSum-=root.val;
        return al;
    }
}




二叉树的深度




递归法:



/**
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 lDepth=TreeDepth(root.left);
        int rDepth=TreeDepth(root.right);
        return 1+(lDepth> rDepth? lDepth: rDepth);
    }
}



二叉平衡树:





public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root==null)
            return true;
        int ldepth=treeDepth(root.left);   //分别求出每个左右节点的深度
        int rdepth=treeDepth(root.right);
        int dif=ldepth-rdepth;             //判断是否超过1
        if(dif<-1 || dif>1)
            return false;
        return IsBalanced_Solution(root.left)&& IsBalanced_Solution(root.right);
    }
    
    public int treeDepth(TreeNode root){
        if(root==null)
            return 0;
        int ldepth=treeDepth(root.left);
        int rdepth=treeDepth(root.right);
        return 1+(ldepth> rdepth? ldepth: rdepth);
    }
}





C++代码:



但是 Java中left 和right 不行(不是指针类型)

故  换用类的属性来代替深度:

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        return isBalanced(root, new Holder());
    }
    //用类和对象的方式
    private class Holder {
        int depth;
    }
    
    boolean isBalanced(TreeNode root, Holder h) {
        if (root == null) {
            h.depth = 0;
            return true;
        }
 
        Holder left = new Holder();  //相当于统计左节点的深度
        Holder right = new Holder();
 
        if (isBalanced(root.left, left) && isBalanced(root.right, right)) {
            int diff=left.depth-right.depth;
            if (diff>=-1 && diff<= 1){
                h.depth =(left.depth > right.depth ? left.depth : right.depth) + 1;
                return true;
            }
        }
        return false;
    }
}

    






二叉树的下一节点:

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


/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;   //为父节点

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
/*分为几种情况
1 若节点为空 则返回空
2 若该节点有右孩子----则下一个节点是其右孩子的最左的孩子
3 若该节点有父节点---是父节点的左孩子 下一节点则是父节点 ----是父节点的右孩子,下一节点则是父节点的父节点-直到其是某个父节点的左孩子
4  如果直到根节点都没有 则返回null
*/
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;  //继续找父节点
        }
        //到根节点了 还没找到 即pNode.next==null
        return null;
    }
}




对称的二叉树:

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

方法1 :用递归的思路
/*
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)
    {
        if(pRoot == null){
            return true;
        }
        return comRoot(pRoot.left, pRoot.right);
    }
    private boolean comRoot(TreeNode left, TreeNode right) {
        if(left == null && right==null)
            return true;
        if(right == null  || left==null) 
            return false;
        //若左右子树值不同  则false;
        if(left.val != right.val) 
            return false;
        //递归比较左子树的左子树和右子树的右子树相同  / 左子树的右子树和右子树的左子树相同
        return comRoot(left.left, right.right) &&comRoot(left.right, right.left) ;
    }
}

方法2 :用非递归的方法(我采用队列思想)
import java.util.LinkedList;
import java.util.Queue;
//采用非递归的做法  因为是前序遍历 --所以用队列来做比较好  根左右
public class Solution {
    boolean isSymmetrical(TreeNode pRoot){
        if(pRoot==null)
            return true;
        //生成两个队列
        Queue<TreeNode> q1=new LinkedList<>();
        Queue<TreeNode> q2=new LinkedList<>();
        TreeNode left;
        TreeNode right;
        //使用队列 --入队根节点左右子节点
        q1.add(pRoot.left);
        q2.add(pRoot.right);
        //开始循环判断  前提是队列非空
        while(!q1.isEmpty() && !q2.isEmpty()){
            left=q1.poll();
            right=q2.poll();
            //将从两个队列中分别弹出的元素进行比较
            if(left==null && right==null){
                continue;                        //注意!!之前直接return true 是不对的,这里是continue
            }
            if(left==null || right==null)
                return false;
            if(left.val!=right.val)
                return false;
            //经过考验的证明当前节点层符合要求
            //继续考察其子节点们
            q1.add(left.left);
            q1.add(left.right);
            q2.add(right.right);
            q2.add(right.left);
        }
    return true;
    }
}




按照之字形打印二叉树

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

方法1 :利用LinkedList的双向性 (构建队列)
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Iterator;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

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

    }

}
*/

/*将每层的数据存进ArrayList中,偶数层时进行reverse操作,
 * 在海量数据时,这样效率太低了。
 * 下面的实现:不必将每层的数据存进ArrayList中,偶数层时进行reverse操作,直接按打印顺序存入
 * 思路:利用Java中的LinkedList的底层实现是双向链表的特点。
 *     1)可用做队列,实现树的层次遍历    
 *     2)可双向遍历,奇数层时从前向后遍历,偶数层时从后向前遍历
 */
public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>>  ret=new ArrayList<>();
        if(pRoot==null)
            return ret;
        //生成队列和每一层记录的列表
        ArrayList<Integer> list=new ArrayList<>();
        LinkedList<TreeNode> queue=new LinkedList<>();
        //定义行分隔符 null  并压入最初的pRoot
        queue.addLast(null);   //每一层起始的分隔符
        queue.addLast(pRoot);
        boolean leftToRight=true ; //定义打印顺序标记
        //循环结束  队列中只有最后一个null值 即所有节点均打印完毕 最后一批是叶子节点没有左右子节点  故只加了null标记
        while(queue.size()!=1){
            //从队列中取出当前首元素
            TreeNode node=queue.removeFirst();
            //如果是null 则证明该打印数据--按照leftToRight标记进行顺序/逆序打印
            if(node==null){
                Iterator<TreeNode> iter=null;
                if(leftToRight)
                    iter=queue.iterator();
                else 
                    iter=queue.descendingIterator();
                //规定好顺序 令标记取反 以便下回使用
                leftToRight=!leftToRight;
                //开始打印null后的所有值
                while(iter.hasNext()){
                    TreeNode temp=(TreeNode)iter.next();
                    list.add(temp.val);   //list记录一层的节点值
                }
                ret.add(new ArrayList<Integer> (list));   // 注意!!!list是引用变量,你所有添加的list都是引用的这一个list
                list.clear();                //清空上次记录的内容
                queue.addLast(null);  //继续记录下层开始的标记
                continue;    //若弹出首节点为null  则不用继续之后压入左右节点的操作 直接继续下次循环
            }
            //若node不是开始标记 则继续压入其非空节点到队列中
            if(node.left!=null){
                queue.addLast(node.left);
            }
            if(node.right!=null){
                queue.addLast(node.right);
            }
        }
        return ret;
    }
}

优化体现在:

1. 这个代码并不是每次new 一个ArrayList用来存储,如果每次new 一个ArrayList,会在堆建立数据,在栈创建地址引用,该代码使用匿名内部类,没有创建栈中的引用,对于大量数据来说,确实省去了很多在栈中的内存开销 

2. 该代码利用Java中的LinkedList的底层实现是双向链表的特点,由于是链表是双向的,所以他在实现反向遍历的时候和正向是一样的


补充:




方法2 :利用Stack栈的特性
public class Solution {
    public static ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        int layer = 1;
        //s1存奇数层节点
        Stack<TreeNode> s1 = new Stack<TreeNode>();
        s1.push(pRoot);
        //s2存偶数层节点
        Stack<TreeNode> s2 = new Stack<TreeNode>();
         
        ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
         
        while (!s1.empty() || !s2.empty()) {
            if (layer%2 != 0) {
                ArrayList<Integer> temp = new ArrayList<Integer>();
                while (!s1.empty()) {
                    TreeNode node = s1.pop();
                    if(node != null) {
                        temp.add(node.val);
                        System.out.print(node.val + " ");
                        s2.push(node.left);
                        s2.push(node.right);
                    }
                }
                if (!temp.isEmpty()) {
                    list.add(temp);
                    layer++;
                    System.out.println();
                }
            } else {
                ArrayList<Integer> temp = new ArrayList<Integer>();
                while (!s2.empty()) {
                    TreeNode node = s2.pop();
                    if(node != null) {
                        temp.add(node.val);
                        System.out.print(node.val + " ");
                        s1.push(node.right);
                        s1.push(node.left);
                    }
                }
                if (!temp.isEmpty()) {
                    list.add(temp);
                    layer++;
                    System.out.println();
                }
            }
        }
        return list;
    }
}








把二叉树打印成多行

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


方法1 : 我沿用上一个题的思路 使用null作为每一层的标记 结合队列来打印
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;
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        //层序遍历 并且按行打印 ---先序遍历
        ArrayList<ArrayList<Integer>>  ret=new ArrayList<>();
        if(pRoot==null)
            return ret;
        ArrayList<Integer>  list=new ArrayList<Integer>();
        LinkedList<TreeNode> queue=new LinkedList<>();
        queue.addLast(null);  //行标记
        queue.addLast(pRoot);
        while(queue.size()!=1){
            TreeNode temp=queue.removeFirst();
            if(temp==null){
                for(TreeNode t: queue){
                    list.add(t.val);
                }
                ret.add(new ArrayList<Integer>(list));
                list.clear();
                queue.addLast(null);
                continue;
            }
            if(temp.left!=null){
                queue.addLast(temp.left);
            }
            if(temp.right!=null){
                queue.addLast(temp.right);
            }
        }
        return ret;
    }
}



方法2 :参考他人思路       队列:  用loc / h 直接限定一层的规模 在此区间内打印
import java.util.LinkedList;
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        //层序遍历 并且按行打印 ---先序遍历
        ArrayList<ArrayList<Integer>>  ret=new ArrayList<>();
        if(pRoot==null)
            return ret;
        ArrayList<Integer>  list=new ArrayList<Integer>();
        LinkedList<TreeNode> queue=new LinkedList<>();
        queue.addLast(pRoot);
        //当队列不为空的情况下
        while(!queue.isEmpty()){
            int loc=0;            //定义当前节点输出位置
            int h=queue.size();  //该层总的规模
            while(loc++<h){     //在当前层上进行顺序打印
                TreeNode temp=queue.removeFirst();
                list.add(temp.val);
                if(temp.left!=null) 
                    queue.addLast(temp.left);
                if(temp.right!=null)
                    queue.addLast(temp.right);
            }
            //一层记录之后  再保存到总的列表中  
            ret.add(new ArrayList<Integer>(list));
            list.clear();  //注意!!一定记得清除每一层打印的节点
        }
        return ret;
    }
}



方法3  :喜欢的思路!!      队列:  记录当前层节点数目  和下一层节点数 并不断更新
import java.util.LinkedList;
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        //层序遍历 并且按行打印 ---先序遍历
        ArrayList<ArrayList<Integer>>  ret=new ArrayList<>();
        if(pRoot==null)
            return ret;
        ArrayList<Integer>  list=new ArrayList<Integer>();
        LinkedList<TreeNode> queue=new LinkedList<>();
        queue.addLast(pRoot);
        //定义当前层节点数
        int current=1;
        //定义下一层节点数
        int nextLev=0;
        while(!queue.isEmpty()){
            TreeNode temp=queue.pop();
            list.add(temp.val);
            current--;      //弹出一个则计数--
            if(temp.left!=null){
                queue.add(temp.left);
                nextLev++;          //新加入一个则下一层节点数目+1
            }
            if(temp.right!=null){
                queue.add(temp.right);
                nextLev++;
            }
            if(current==0){   //当当前层节点全部打印完,则用ret记录一层节点结果
                current=nextLev;   //并将当前层数更新为下一层数目
                nextLev=0;         //下一层又重新恢复到0
                ret.add(new ArrayList<Integer>(list));
                list.clear();
            }
        }
        return ret;
    }
}



相关的有 打印每层行号问题















序列化二叉树:

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



方法1:  通过先序遍历 实现(递归做法)  超级简单






/*
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 {
    //序列化 先序遍历---每次读完节点j加!结束标志  若节点为空则#!
    String Serialize(TreeNode root) {
        if(root==null){
            return "#!";     //如果根节点为空 则直接返回#!表示
        }
        String res=root.val+"!";   //根节点
        res+=Serialize(root.left);    //左节点
        res+=Serialize(root.right);   //右节点
        return res;
    }
    
    //反序列化的过程就是将字符串-->字符串数组--->重新构建一棵树
    TreeNode Deserialize(String str) {
        String [] tree=str.split("!");     //将字符串根据分隔符号进行数组划分
        LinkedList<String>  queue=new LinkedList<>();   
        for(int i=0;i<tree.length;i++){
            queue.add(tree[i]);           //用队列进行记录
        }
        return initialTree(queue);
    }
    
    public TreeNode initialTree(LinkedList<String> queue){    //重新构建树的函数
        String temp=queue.poll();
        if(temp.equals("#")){     //若弹出元素为#则代表此节点为空
            return null;
        }
        TreeNode head=new TreeNode(Integer.valueOf(temp));    //构造根节点
        head.left=initialTree(queue);     //然后是左节点
        head.right=initialTree(queue);   //最后是右节点部分
        return head;
    }
}



方法2:  通过层序遍历   实现(递归做法)  超级简单




import java.util.LinkedList;
public class Solution {
    //序列化 先序遍历---每次读完节点j加!结束标志  若节点为空则#! 使用层序遍历的思想  循环解决问题
    String Serialize(TreeNode root) {
        if(root==null){
            return "#!";
        }
        String res=root.val+"!";
        LinkedList<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){ 
            TreeNode temp=queue.poll();
            if(temp.left!=null){
                res=res+temp.left.val+"!";
                queue.add(temp.left);
            }else{
                res+="#!";
            }
            if(temp.right!=null){
                res=res+temp.right.val+"!";
                queue.add(temp.right);
            }else{
                res+="#!";
            }
        }
       return res;
    }
     TreeNode Deserialize(String str) {
         String [] values=str.split("!");
         int index=0;
         TreeNode head=generateNodeByString(values[index++]);
         LinkedList<TreeNode> queue=new LinkedList<>();
         if(head!=null){
             queue.offer(head);
         }
         TreeNode node=null;
         while(!queue.isEmpty()){
             node=queue.poll();
             node.left=generateNodeByString(values[index++]);
             node.right=generateNodeByString(values[index++]);
             if(node.left!=null){
                 queue.offer(node.left);
             }
             if(node.right!=null){
                 queue.offer(node.right);
             }
         }
         return head;
     }
    
    public TreeNode generateNodeByString (String val){
        if(val.equals("#"))
            return null;
        return new TreeNode(Integer.valueOf(val));
    }
}





二叉搜索树的第k个结点

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

方法1 :二叉搜索树的中序遍历(也就是按照顺序打印)  “递归”的思路
public class Solution {
    int count=0;   //定义计数器
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(pRoot==null || k==0)
            return null;
        //根节点不为空   从其左节点开始
        TreeNode node=KthNode(pRoot.left, k);
        if(node!=null)
            return node;
        if(++count==k)   //如果找到第K个顺序节点 则直接返回当前节点
            return pRoot;    
        //左节点未找到  根也没对上 则找寻右节点
        node=KthNode(pRoot.right,k);
        if(node!=null)
            return node;
        return null;
    }
}

方法2 :完全模拟中序遍历的“非递归”方式 适当修改
import java.util.Stack;
public class Solution {
    int count=0;   //定义计数器
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(pRoot==null || k==0)
            return null;
        //采用非递归去做 完全类似于非递归的中序遍历
        Stack<TreeNode>  s=new Stack<TreeNode>();
        while(pRoot!=null || !s.isEmpty()){
            if(pRoot!=null){
                s.push(pRoot);
                pRoot=pRoot.left ;  //不断找最左节点
            }else{
                pRoot=s.pop();
                count++;
                if(count==k)
                    return pRoot;
                pRoot=pRoot.right;
            }
        }
        return null;
    }
}


数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。


方法1:  用优先队列模拟两个堆,一个最大堆,一个最小堆 。 

时间:nO(log(n/2))         空间O(log(n/2))


/***********方式一、用两个优先队列来模拟两个堆---主要思路************************
    1.先用java集合PriorityQueue来设置一个小顶堆和大顶堆,大顶堆需要先重写一下里面的比较器
    2.主要的思想是:因为要求的是中位数,那么这两个堆,大顶堆用来存较小的数,从大到小排列;
                                              小顶堆存较大的数,从小到大的顺序排序,
                                              显然中位数就是大顶堆的根节点与小顶堆的根节点和的平均数。
    保证:小顶堆中的元素都大于等于大顶堆中的元素,所以每次塞值,并不是直接塞进去,而是从另一个堆中poll出一个最大(最小)的塞值
    3.当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中;
      当数目为奇数的时候,将这个值插入小顶堆中,再讲小顶堆中根节点(即最小值)插入到大顶堆中
      这样就可以保证,每次插入新值时,都保证小顶堆中值大于大顶堆中的值,并且都是有序的。
    4.由于第一个数是插入到小顶堆中的,所以在最后取中位数的时候,若是奇数,就从小顶堆中取即可。
    这样,当count为奇数的时候,中位数就是小顶堆的根节点;当count为偶数的时候,中位数为大顶堆和小顶堆两个根节点之和的平均数
    5.例如,传入的数据为:[5,2,3,4,1,6,7,0,8],那么按照要求,输出是"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
        a.那么,第一个数为5,count=0,那么存到小顶堆中,
            步骤是:先存到大顶堆;然后弹出大顶堆root,就是最大值给小顶堆,第一次执行完,就是小顶堆为5,count+1=1;    
            此时若要输出中位数,那么就是5.0,因为直接返回的是小顶堆最小值(第一次塞入到小顶堆中,是从大顶堆中找到最大的给他的)
        b.继续传入一个数为2,那么先存到小顶堆中,将小顶堆最小值弹出给大顶堆,即2,那么这次执行完,小顶堆为5,大顶堆为2,count+1=2
            此时若要输出中位数,因为是偶数,那么取两个头的平均值,即(5+2)/2=3.5(第二次塞入到大顶堆中,是从小顶堆中找到最小的给他的)
        c.继续传入一个数为3,那么此时count为偶数,那么执行第一个if,先存到大顶堆中,大顶堆弹出最大值,那么3>2,就是弹出3
            3存到小顶堆中,那么此时小顶堆为3,5,大顶堆为2,count+1=3(第三次塞入到小顶堆中,是从大顶堆中找到最大的给他的)
            此时若要输出中位数,因为是奇数,那么取小顶堆的最小值,即3.0
        d.继续传入一个数为4,先存到小顶堆中,小顶堆此时为3,4,5,弹出最小值为3,给大顶堆
            此时大顶堆为3,2,小顶堆为4,5,(第四次塞入到小顶堆中,是从大顶堆中找到最大的给他的)
            此时若要输出中位数,因为是偶数,那么取两个头的平均值,即(3+4)/2=3.5
        e.依次类推。。。

import java.util.PriorityQueue; 
import java.util.Comparator;
public class Solution {
    //建立一个计数器  统计奇偶数
    int count=0;
    //建立两个堆去处理数据   相当于将数据流分为两部分(按照大   小 顺序)
    //优先队列默认是升序(最小堆构造)--要想改为最大堆则需要将比较的方法进行更改
    PriorityQueue<Integer>  minHeap=new PriorityQueue<>();
    PriorityQueue<Integer>  maxHeap=new PriorityQueue<>(new Comparator<Integer>(){
        public int compare(Integer o1, Integer o2){
            return o2-o1; 
        }
    });
    //新增数据
    public void Insert(Integer num) {
        //偶数情况下  数据经最大堆---到最小堆--连接成一个数组(前半段--后半段)
        if(count%2==0){
            maxHeap.offer(num);
            Integer temp=maxHeap.poll();
            minHeap.offer(temp);
        }else{    //为奇数时  数据经最小堆---最大堆--同样连接成一个数组--中位数在最小堆里
            minHeap.offer(num);
            Integer temp=minHeap.poll();
            maxHeap.offer(temp);
        }
        count++;   //存入一个则计数一次
    }
    //获取元素
    public Double GetMedian() {
        if(count%2==0){
            return new Double(maxHeap.peek()+minHeap.peek())/2;
        }else{
            return new Double(minHeap.peek());
        }
    }
}


方法2: 不用堆   直接用列表  每次加入一个元素总是排序  
/***************方式二、ArrayList***********************
    用ArrayList来存输入的数据流,然后每次用Collections.sort(list)来保证数据流有序,然后再取中位数
    思想非常简单,但是每次都要进行排序,时间复杂度可想而知
时间O(nlogn)*n   空间O(n)
import java.util.*;
public class Solution {
    ArrayList<Integer> list = new ArrayList<Integer>();
    public void Insert(Integer num) {    
        list.add(num);
        Collections.sort(list);   //每次插入新元素总是要排序
    }
    public Double GetMedian() {
        int mid = list.size() / 2;    //选择中间位置            
        if((list.size()&1) == 1)      return list.get(mid)/1.0;    //奇数情况下
        else                          return (list.get(mid-1) + list.get(mid))/2.0;    //偶数情况下     
    }
}


方法3: 插入排序
/***************方式三、插入排序,插入到对应的位置***********************
    LinkedList<Integer> data = new LinkedList<Integer>();
    public void Insert(Integer num) {
        for (int i = data.size() - 1; i >= 0 ; i--) {
            if (num >= data.get(i)){
                data.add(i+1,num);
                return;
            }
        }
        data.addFirst(num);
    }
    ****************************************/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值