4-树篇(8)

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

分析:根据示例可以知道,前序遍历序列第1个为根节点,再根据中序遍历序列可以得到根节点的左右子树{472},{5386};由前序遍历左子树{2 4 7}可以直到左子树根节点为2,再根据中序遍历左子树{472}可以再次分为左右子树……依次递推;

拓展:Arrays.copyOfRange(arr, i, j);复制数组,包括索引i,不包括索引j;

 1 /**
 2  * Definition for binary tree
 3  * public class TreeNode {
 4  *     int val;
 5  *     TreeNode left;
 6  *     TreeNode right;
 7  *     TreeNode(int x) { val = x; }
 8  * }
 9  */
10 import java.util.Arrays;
11 public class Solution {
12     public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
13         if(pre.length==0||in.length==0) return null;
14         TreeNode root = new TreeNode(pre[0]);
15         for(int i=0;i<pre.length;i++){
16             if(pre[0]==in[i]){//每次递归到此处,pre,in变成原数组的子集;可重新看作是新的前序后序遍历
17                 root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));
18                 root.right = reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,pre.length));
19                 break;
20             }
21         }
22         return root;
23     }
24 }

 

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

 

 分析:两种情况:当前节点

  1.有右节点:判断右节点是否有左子树;①如果有左子树,则返回左子树最左下方节点

                    ②如果没有左子树,则返回右节点

  2.没有右节点:判断当前节点是父节点的左孩子还是右孩子;

    ①如果是左孩子,则返回父节点

    ②如果是右孩子,向上遍历祖先节点,直到找到一个祖先节点满足为左孩子的条件,返回该祖先节点的父节点,否则返回null了,当前节点为尾节点;

 1 /*
 2 public class TreeLinkNode {
 3     int val;
 4     TreeLinkNode left = null;
 5     TreeLinkNode right = null;
 6     TreeLinkNode next = null;
 7 
 8     TreeLinkNode(int val) {
 9         this.val = val;
10     }
11 }
12 */
13 public class Solution {
14     public TreeLinkNode GetNext(TreeLinkNode pNode)
15     {
16         //第一种情况:判断当前节点有右节点(针对从B->H这种情况)
17         if(pNode.right!=null){
18             pNode = pNode.right;
19             while(pNode.left!=null){
20                 pNode = pNode.left;
21             }
22             return pNode;
23         }
24         //第二种情况,当前节点没有右节点
25         //判断当前节点是(父节点的)左孩子还是右孩子
26         //1.如果是根节点直接null
27         if(pNode.next==null) return null;
28         //2.如果左孩子
29         if(pNode.next.left==pNode){//如果当前节点是左孩子
30             return pNode.next;//直接返回父节点
31         }
32         //3.如果是右孩子
33         TreeLinkNode pFather = pNode.next;
34         while(pFather.next!=null&&pFather!=pFather.next.left){//向上遍历祖先节点,直到找到一个祖先节点满足为左孩子的条件
35              pFather = pFather.next;
36         }
37         return pFather.next;//返回该祖先节点的父节点
38     }
39 }

 

 

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

 分析:使用递归,逐层对比;

   关键点:return isSymmetrical(root1.left,root2.right)&&isSymmetrical(root1.right,root2.left); 将root1的左节点和root2的右节点比较,root1的右节点和root2的左节点比较(对称性)

 1 /*
 2 public class TreeNode {
 3     int val = 0;
 4     TreeNode left = null;
 5     TreeNode right = null;
 6 
 7     public TreeNode(int val) {
 8         this.val = val;
 9 
10     }
11 
12 }
13 */
14 public class Solution {
15     boolean isSymmetrical(TreeNode pRoot)
16     {
17         if(pRoot==null) return true;
18         return isSymmetrical(pRoot.left,pRoot.right);
19     }
20     boolean isSymmetrical(TreeNode root1, TreeNode root2){
21         if(root1==null&&root2==null){
22             return true;
23         } else if(root1==null||root2==null){
24             return false;
25         }
26         if(root1.val == root2.val){
27             return isSymmetrical(root1.left,root2.right)&&isSymmetrical(root1.right,root2.left);
28         }else{
29             return false;
30         }
31     }
32 }

 

 

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

 A C B D E F G I H

分析:①奇数行-从左到右遍历放入Stack1中,取出是先遍历右节点,再遍历左节点;(注意:这里是节点存入栈中,并不是取出结果,想要结果正    确,存入栈中得反着存)

      ②偶数行-从右到左遍历,先存入Stack2中,取出是先遍历左节点,再遍历右节点;

    ③奇数行or偶数行判断:可设置一个变量isOdd,每次Stack1或者Stack2为空时(这行遍历完了)就递增;

 扩展:Stack API

 

 

 

 

 1 import java.util.*;
 2 
 3 /*
 4 public class TreeNode {
 5     int val = 0;
 6     TreeNode left = null;
 7     TreeNode right = null;
 8 
 9     public TreeNode(int val) {
10         this.val = val;
11 
12     }
13 
14 }
15 */
16 public class Solution {
17     public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
18         ArrayList<ArrayList<Integer> > list = new ArrayList<ArrayList<Integer> >();
19         Stack<TreeNode> stack1 = new Stack<TreeNode>();//存放奇数行节点
20         Stack<TreeNode> stack2 = new Stack<TreeNode>();//存放偶数行节点(从0开始计算)
21         stack2.push(pRoot);
22         int isOdd = 1;
23         while(!stack1.empty()||!stack2.empty()){
24             if(isOdd%2!=0){//遍历到奇数行
25                 ArrayList<Integer> temp = new ArrayList<Integer>();
26                 while(!stack2.empty()){//根据上一行(偶数行)得到奇数层的遍历结果
27                     TreeNode node = stack2.pop();
28                     if(node!=null){
29                         temp.add(node.val);
30                         stack1.push(node.left);//先左后右(这里是放入stack中,先进后出,如果想要结果先右后左,就必须反着存入栈中)
31                         stack1.push(node.right);
32                     }
33                 }
34                 if(!temp.isEmpty()){
35                     list.add(temp);
36                     isOdd++;
37                 }
38             }else{
39                 ArrayList<Integer> temp = new ArrayList<Integer>();
40                 while(!stack1.empty()){//根据上一行(偶数行)得到奇数层的遍历结果
41                     TreeNode node = stack1.pop();
42                     if(node!=null){
43                         temp.add(node.val);
44                         stack2.push(node.right);
45                         stack2.push(node.left);
46                     }
47                 }
48                 if(!temp.isEmpty()){
49                     list.add(temp);
50                     isOdd++;
51                 }
52             }
53         }
54         return list;
55     }
56 }

 

 

题五:【把二叉树打印成多行】从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

分析:层序遍历BST-使用队列;(我的方法是利用题四思想,直接使用两个队列完成,相较于题四难度降低)

注意:本题难点在于解决层数信息

拓展:队列API

 

 

 1 import java.util.*;
 2 
 3 
 4 /*
 5 public class TreeNode {
 6     int val = 0;
 7     TreeNode left = null;
 8     TreeNode right = null;
 9 
10     public TreeNode(int val) {
11         this.val = val;
12 
13     }
14 
15 }
16 */
17 public class Solution {
18     ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
19         ArrayList<ArrayList<Integer> > list = new ArrayList<ArrayList<Integer> >();
20         Queue<TreeNode> queue1 = new LinkedList<TreeNode>();
21         Queue<TreeNode> queue2 = new LinkedList<TreeNode>();
22         queue1.offer(pRoot);
23         int isOdd = 0;//层数
24         while(queue1.size()!=0||queue2.size()!=0){
25             if(isOdd%2==0){
26                 ArrayList<Integer> temp = new ArrayList<Integer>();
27                 while(queue1.size()!=0){
28                     TreeNode node = queue1.poll();
29                     if(node!=null){
30                         temp.add(node.val);
31                         queue2.offer(node.left);
32                         queue2.offer(node.right);
33                     }
34                 }
35                 if(!temp.isEmpty()){
36                     isOdd++;
37                     list.add(temp);
38                 }
39             }else{
40                 ArrayList<Integer> temp = new ArrayList<Integer>();
41                 while(queue2.size()!=0){
42                     TreeNode node = queue2.poll();
43                     if(node!=null){
44                         temp.add(node.val);
45                         queue1.offer(node.left);
46                         queue1.offer(node.right);
47                     }
48                 }
49                 if(!temp.isEmpty()){
50                     isOdd++;
51                     list.add(temp);
52                 }
53             }
54         }
55         return list;
56     }
57 }

 

 

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

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

 

 1 /*
 2 public class TreeNode {
 3     int val = 0;
 4     TreeNode left = null;
 5     TreeNode right = null;
 6 
 7     public TreeNode(int val) {
 8         this.val = val;
 9 
10     }
11 
12 }
13 */
14 import java.util.*;
15 public class Solution {
16     String Serialize(TreeNode root) {
17         //前序遍历
18         if(root==null){
19             return "#!";
20         }
21         String str = root.val+"!";
22         str += Serialize(root.left);
23         str += Serialize(root.right);
24         return str;
25   }
26     /**
27      * 将序列化字符串装入一个queue中
28      */
29     TreeNode Deserialize(String str) {
30         Queue<String> queue = new LinkedList<>();
31         Collections.addAll(queue, str.split("!"));
32         return Deserialize(queue);
33     }
34     TreeNode Deserialize(Queue<String> queue) {
35         String str = queue.poll();
36         if(str.equals("#")){
37             return null;
38         }
39        TreeNode root = new TreeNode(Integer.parseInt(str));
40        root.left = Deserialize(queue);
41         root.right = Deserialize(queue);
42         return root;
43   }
44 }

 

 

题七:【二叉搜索树的第k个节点】给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4。

分析:使用中序遍历二叉搜索树,放在集合里,根据k值直接取出

 1 /*
 2 public class TreeNode {
 3     int val = 0;
 4     TreeNode left = null;
 5     TreeNode right = null;
 6 
 7     public TreeNode(int val) {
 8         this.val = val;
 9 
10     }
11 
12 }
13 */
14 import java.util.*;
15 public class Solution {
16     ArrayList<TreeNode> list = new ArrayList<TreeNode>();
17     TreeNode KthNode(TreeNode pRoot, int k)
18     {
19         if(k<=0) return null;
20         KthNode(pRoot);
21         if(list.size()<k) return null;
22         return list.get(k-1);
23     }
24     void KthNode(TreeNode node){
25         if(node==null) return;
26         KthNode(node.left);
27         list.add(node);
28         KthNode(node.right);
29     }
30 }

 

 

题八:【数据流中的中位数】

 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

 用例:[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 "

分析:暴力破解-使用集合存储输入,之后排序,最后直接跟据索引取出结果

 1 import java.util.*;
 2 public class Solution {
 3     ArrayList<Integer> list = new ArrayList<Integer>();
 4     public void Insert(Integer num) {
 5         list.add(num);
 6     }
 7 
 8     public Double GetMedian() {
 9         int len = list.size();
10         Collections.sort(list);
11         if(len%2!=0){
12             return Double.valueOf(list.get(len/2));
13         }else{
14             int index1 = (len-1)/2;
15             return (Double.valueOf(list.get(index1))+Double.valueOf(list.get(index1+1)))/2;
16         }
17     }
18 }

 

分析:中位数左边都是比中位数小的数,右边都是比中位数大的数。可以使用大顶堆存放左边的数据,小顶堆存放右边的数据。可以快速的找到左边的最大值和右边的最小值(堆顶元素)。往堆中插入数据时间复杂度O(logn),得到堆顶元素时间复杂度O(1)。

注意:保证大顶堆和小顶堆的数目之差不能超过1,并且保证大顶堆的数据小于小顶堆的数据。如果插入一个数比大顶堆的数据小,则将此数据加入大顶堆,并将大顶堆堆顶数据加入到小顶堆,反之亦然。

拓展:PriorityQueue API

 

Java的PriorityQueue 是从JDK1.5开始提供的新的数据结构接口,默认内部是自然排序,结果为小顶堆,也可以自定义排序器,比如下面反转比较,完成大顶堆。

 

 

 

 

 

 1 import java.util.Comparator;
 2 import java.util.PriorityQueue;
 3 public class Solution {
 4     PriorityQueue<Integer> minQueue = new PriorityQueue<Integer>();
 5     PriorityQueue<Integer> maxQueue = new PriorityQueue<Integer>(new Comparator<Integer>(){
 6         public int compare(Integer o1, Integer o2){
 7             //PriorityQueue默认是小顶堆,实现大顶堆,需要反转默认排序器
 8             return o2-o1;
 9         }
10     });
11     int count = 0;//数据总数量
12     public void Insert(Integer num) {
13         count++;
14         //数据必须在大顶堆和小顶堆中各倒一次,保证大顶堆的数据都比小顶堆的数据小;
15         if(count%2!=0){//奇数,添加到大顶堆,然后将大顶堆的堆顶添加到小顶堆
16             maxQueue.offer(num);
17             minQueue.offer(maxQueue.poll());
18         }else{//偶数,添加到小顶堆,并将小顶堆堆顶数据添加到大顶堆
19             minQueue.offer(num);
20             maxQueue.offer(minQueue.poll());
21         }
22     }
23   //可使用示例分析
24     public Double GetMedian() {
25         if(count%2!=0){//如果是奇数
26             return Double.valueOf(minQueue.peek());
27         }else{
28             return Double.valueOf((minQueue.peek()+maxQueue.peek()))/2;
29         }
30     }
31 }

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-12-09 15:02  Qmillet  阅读(255)  评论(0编辑  收藏  举报