编码解决二叉树问题能够帮助我们深入理解递归思想,并能提高代码能力。在这里总结上一周实现的五个小例题,从《程序员代码面试指南》里面挑选出来的。它们是:二叉树的序列化和反序列化、找到二叉树中指定值的最长路径长度、找到二叉树中最大的二叉搜索子树、层序打印、搜索二叉树中两个错误的节点和判断二叉树r1是否包含二叉树r2的全部拓扑结构。
1、二叉树的序列化和反序列化
代码包含二叉树的先序序列化、先序反序列化,以及层序的序列化、层序的反序列化。下面是代码部分:
//先序序列化这棵树
public static String serialByPre(Node root) {//递归实现二叉树的先序序列化
if (root == null) {
return "#!";
}
String res = root.data + "!";
res += serialByPre(root.left);
res += serialByPre(root.right);
return res;
}
public static Node reconByPreString(String str) {//字符串反序列化二叉树
String[] vals = str.split("!");
Queue<String> queue = new LinkedList<String>();
for (int i = 0; i < vals.length; i++) {
queue.offer(vals[i]);
}
return reconPreOrder(queue);
}
private static Node reconPreOrder(Queue<String> queue) {//对字符串队列,使用递归方法将其反序列化为二叉树
String value = queue.poll();
if (value.equals("#")) {
return null;
}
Node head = new Node(Integer.parseInt(value));
head.left = reconPreOrder(queue);
head.right = reconPreOrder(queue);
return head;
}
//层序序列化和反序列化
public static String serialByLevel(Node root){
if(root == null){
return "#!";
}
Queue<Node> queue = new LinkedList<Node>();
String ret = root.data+"!";
queue.offer(root);
while(!queue.isEmpty()){
Node top = queue.poll();
if(top.left != null){
ret+=top.left.data+"!";
queue.offer(top.left);
}else{
ret+="#!";
}
if(top.right != null){
ret+=top.right.data+"!";
queue.offer(top.right);
}else{
ret+="#!";
}
}
return ret;
}
public static Node reconByLevelString(String str){
String[] items = str.split("!");
int index =0;
Queue<Node> queue = new LinkedList<Node>();//队列暂存Node
Node head = getNode(items[index++]);
queue.offer(head);
while(!queue.isEmpty()){
Node top = queue.poll();
top.left=getNode(items[index++]);
top.right=getNode(items[index++]);
if(top.left != null){
queue.offer(top.left);
}
if(top.right != null){
queue.offer(top.right);
}
}
return head;
}
private static Node getNode(String string) {
if(!string.equals("#")){
return new Node(Integer.parseInt(string));
}
return null;
}
2、找到二叉树中指定值的最长路径长度
给定一个指定的路径长度,二叉树路径中跨越层数的最大值。
// 在二叉树中找到累加和为指定值的最长路径长度
public static int getMaxLength(Node root,int sum){//root-根节点,sum-指定的值
HashMap<Integer,Integer> sumMap = new HashMap<Integer,Integer>();
sumMap.put(0, 0);//路径长度为0的最大路径长度为0
return preOrder(root,sum,0,1,0,sumMap);
}
private static int preOrder(Node root, int sum, int preSum, int level,
int maxLen, HashMap<Integer, Integer> sumMap) {//preSum-记录上次统计的值,level-二叉树层数,maxLen-最大路径长度,sumMap-中间变量保存队列
if(root == null){
return maxLen;
}
int curLen = preSum + root.data;
if(!sumMap.containsKey(curLen)){
sumMap.put(curLen, level);
}
if(sumMap.containsKey(curLen-sum)){
maxLen = Math.max(level-sumMap.get(curLen-sum), maxLen);
}
maxLen=preOrder(root.left,sum,curLen,level+1,maxLen,sumMap);
maxLen=preOrder(root.right,sum,curLen,level+1,maxLen,sumMap);
return maxLen;
}
3、找到二叉树中最大的二叉搜索子树
在二叉树中找到最大的二叉搜索子树,并返回该二叉树搜索子树的头结点。
// 找到二叉树中最大的搜索二叉子树
public static Node getMaxSub(Node root){
if(root == null){
return null;
}
int[] recorde = new int[3];
return posOrder(root,recorde);
}
private static Node posOrder(Node root, int[] recorde) {
/*
二叉树后序遍历解决问题。
recorde记录中间信息,0号位置记录节点个数,1号位置记录最小值,2号位置记录最大值。
*/
if(root == null){
recorde[0]=0;
recorde[1]=Integer.MAX_VALUE;
recorde[2]=Integer.MIN_VALUE;
return null;
}
int data = root.data;
Node left = root.left;
Node right = root.right;
Node lBST = posOrder(left,recorde);
int lsize = recorde[0];
int lmin = recorde[1];
int lmax = recorde[2];
Node rBST = posOrder(right,recorde);
int rsize = recorde[0];
int rmin = recorde[1];
int rmax = recorde[2];
recorde[1]=Math.min(lmin,data);
recorde[2]=Math.max(rmax, data);
if(left == lBST && right ==rBST && data>lmax && data<rmin){
recorde[0]=lsize+rsize +1;
return root;
}
recorde[0]=Math.max(lsize, rsize);
return lsize >rsize?lBST:rBST;
}
4、层序打印
//顺序层序打印
public static void printByLevel(Node root){
if(root == null){
System.out.println("#");
}
Queue<Node> queue = new LinkedList<Node>();
queue.offer(root);
Node last = root;
Node nlast = root;
while(!queue.isEmpty()){
Node item = queue.poll();
System.out.print(item.data+" ");
if(item.left != null){
nlast = item.left;
queue.offer(item.left);
}
if(item.right != null){
nlast = item.right;
queue.offer(item.right);
}
if(item == last){
System.out.println();
last = nlast;
}
}
}
//ZigZag方式层序打印二叉树
public static void printByLevelZig(Node root){
if(root == null){
System.out.println("#");
}
Queue<Node> queue = new LinkedList<Node>();
Queue<Integer> tmp = new LinkedList<Integer>();
queue.offer(root);
Node last = root;
Node nlast = root;
boolean flag = true;
while(!queue.isEmpty()){
Node item = queue.poll();
//System.out.print(item.data+" ");
tmp.offer(item.data);
if(item.left != null){
nlast = item.left;
queue.offer(item.left);
}
if(item.right != null){
nlast = item.right;
queue.offer(item.right);
}
if(item == last){
pirntQueue(tmp,flag);
flag = !flag;
last = nlast;
}
}
}
private static void pirntQueue(Queue<Integer> tmp,boolean flag) {
if(flag){
while(!tmp.isEmpty()){
System.out.print(tmp.poll()+" ");
}
}else{
Object[] arry = new Object[tmp.size()];
arry=tmp.toArray();
int length = arry.length;
for(int i=0; i<length;i++){
System.out.print((int)arry[length-1-i]+" ");
}
tmp.clear();
}
System.out.println();
}
5、搜索二叉树中两个错误的节点
//调整搜索二叉树中两个错误的节点
public static Node[] getTwoErrNodes(Node root){
/*
中序遍历方式,找到局部逆序的两个位置,找到两个错误节点。
*/
Node[] errs = new Node[2];
if(root == null){
return errs;
}
Stack<Node> stack = new Stack<Node>();
Node pre = null;
while(!stack.isEmpty() || root !=null){
if(root != null){
stack.push(root);
root = root.left;
}else{
root = stack.pop();
if(pre != null && pre.data > root.data){
errs[0]=errs[0]==null?pre:errs[0];
errs[1]=root;
}
pre=root;
root=root.right;
}
}
return errs;
}
6、判断二叉树r1是否包含二叉树r2的全部拓扑结构
//判断二叉树r1是否包含二叉树r2的全部拓扑结构
public static boolean contains(Node r1,Node r2){
/*
递归方式解决问题,将问题转化为父节点和子节点问题。然后,针对左子树和右子树去解决问题。
*/
if(r1 != null)
return check(r1,r2)||contains(r1.left,r2)||contains(r1.right,r2);
else
return false;
}
private static boolean check(Node r1, Node r2) {
if(r2 == null){
return true;
}
if(r1 == null || r1.data != r2.data){
return false;
}
return check(r1.left,r2.left) && check(r1.right,r2.right);
}