题一:【重建二叉树】输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{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 }
题六:【序列化二叉树】请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果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
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(maxQueue.peek()); 27 }else{ 28 return Double.valueOf((minQueue.peek()+maxQueue.peek()))/2; 29 } 30 } 31 }
来源:https://www.cnblogs.com/qmillet/p/12000557.html