剑指offer:面试题6--重建二叉树
前言:文章是加深自己对书的理解和自身的代码书写能力,如果有错误的地方请指正,万分感谢。
问题描述:根据二叉树的中序遍历和前序遍历或者中序遍历和后续遍历恢复出树的拓扑形状。
单独实现这个算法并不困难,我已经使用C#代码实现了,把它封装在OrderLaboratory的类中,返回的是树的根节点,上王道解释可能比较清楚一点。
IOrderDoctor是一个接口,主要有两个方法一个是绑定数据,前序和中序或者中序和后续的序列,另一个则是返回相应的根节点。
public interface IOrderLaboratory { /// <summary> /// 数据不合法返回null /// </summary> /// <param name="orderDataA">the pre order values</param> /// <param name="orderDataB">the in order values </param> /// <returns></returns> TreeNode PreInConstructor(char[] orderDataA, char[] orderDataB); /// <summary> /// /// </summary> /// <param name="orderDataA">the post order values</param> /// <param name="orderDataB">the in order values </param> /// <returns></returns> TreeNode PostInConstructor(char[] orderDataA, char[] orderDataB); }
OrderDoctor是一个抽象类,除了检查绑定的数据是否合理,还有是有一个abstract TreeNode ConstructCore(int a_start,int a_end,int b_start,int b_end) ;让它的子类分别实现对应的ConstructCore函数,以构造前序和中序或者中序和后续的树节点。
public abstract class OrderDoctor:IOrderDoctor { protected char[] _orderDataA; protected char[] _orderDataB; public OrderDoctor() { _orderDataA = null; _orderDataB = null; } public void Binding(char[] orderDataA, char[] orderDataB) { if (orderDataA == null || orderDataB == null || orderDataA.Length < 1 || orderDataB.Length < 1) { Console.WriteLine("没有数据!!!"); _orderDataA = null; _orderDataB = null; } else if (orderDataA.Length != orderDataB.Length) { Console.WriteLine("数据长短不一致!!!"); _orderDataA = null; _orderDataB = null; } else { _orderDataA = orderDataA; _orderDataB = orderDataB; } } public TreeNode ConstructTree() { if (_orderDataA == null || _orderDataB == null) { Console.WriteLine("请绑定正确的序列"); return null; } else { return ConstructCore(0,_orderDataA.Length-1,0,_orderDataB.Length-1); } } protected abstract TreeNode ConstructCore(int a_start,int a_end,int b_start,int b_end) ; }
IOrderLaboratory接口表示直接对外提供根据前序和中序或者中序和后续的序列,返回树节点的功能。由OrderLaboratory类实现,通过IOrderDoctor接口实例化PreAndInOrderDoctor 和PostAndInOrderDoctor 对象。
public class TreeNode : IComparable<TreeNode> { public TreeNode(char ch) { _value = ch; LeftSon = null; RightSon = null; Father = null; } /// <summary> /// 根节点为1,子节点每次递加1. /// </summary> public int _level; private char _value; public char Value { get { return _value; } set { _value = value; } } public bool IsLeafNode { get { if (LeftSon == null && RightSon == null) return true; else return false; } } public int _leftChildrenCount; public int _rightChildrenCount; public TreeNode LeftSon; public TreeNode RightSon; public TreeNode Father; public int CompareTo(TreeNode obj) { return _value.CompareTo(obj._value); } }
interface IOrderDoctor { /// <summary> /// orderDataA is PreOrder or PostOrder /// orderDataB is always InOrder /// </summary> /// <param name="orderDataA"></param> /// <param name="orderDataB"></param> void Binding(char []orderDataA,char []orderDataB); /// <summary> /// 返回根节点 /// </summary> /// <returns></returns> TreeNode ConstructTree(); } public abstract class OrderDoctor:IOrderDoctor { protected char[] _orderDataA; protected char[] _orderDataB; public OrderDoctor() { _orderDataA = null; _orderDataB = null; } public void Binding(char[] orderDataA, char[] orderDataB) { if (orderDataA == null || orderDataB == null || orderDataA.Length < 1 || orderDataB.Length < 1) { Console.WriteLine("没有数据!!!"); _orderDataA = null; _orderDataB = null; } else if (orderDataA.Length != orderDataB.Length) { Console.WriteLine("数据长短不一致!!!"); _orderDataA = null; _orderDataB = null; } else { _orderDataA = orderDataA; _orderDataB = orderDataB; } } public TreeNode ConstructTree() { if (_orderDataA == null || _orderDataB == null) { Console.WriteLine("请绑定正确的序列"); return null; } else { return ConstructCore(0,_orderDataA.Length-1,0,_orderDataB.Length-1); } } protected abstract TreeNode ConstructCore(int a_start,int a_end,int b_start,int b_end) ; } public class PreAndInOrderDoctor : OrderDoctor { protected override TreeNode ConstructCore(int pre_start, int pre_end, int in_start, int in_end) { char rootValue = _orderDataA[pre_start]; TreeNode root = new TreeNode(rootValue); root.Father=root.LeftSon = root.RightSon = null; if(pre_start==pre_end) { if (in_start == in_end && _orderDataA[pre_start] == _orderDataB[in_start]) return root; else { Console.WriteLine("数据输入出错!请确保输入的是前序和中序序列"); throw new Exception("数据输入出错"); } } int i = in_start; while (i <= in_end && _orderDataB[i] != rootValue) i++; if(i>in_end) { Console.WriteLine("数据输入出错!请确保输入的是前序和中序序列"); throw new Exception("数据输入出错"); } int leftSonCount = i - in_start; int rightSonCount = in_end - i; if (leftSonCount > 0) { root.LeftSon = ConstructCore(pre_start + 1, pre_start + leftSonCount, in_start, i - 1); if (root.LeftSon!=null) root.LeftSon.Father = root; } if(rightSonCount>0) { root.RightSon = ConstructCore(pre_end-rightSonCount+1,pre_end,i+1,in_end); if (root.RightSon != null) root.RightSon.Father = root; } return root; } } public class PostAndInOrderDoctor : OrderDoctor { /// <summary> /// 后序遍历,最后一个是首节点 /// </summary> /// <param name="post_start"></param> /// <param name="post_end"></param> /// <param name="in_start"></param> /// <param name="in_end"></param> /// <returns></returns> protected override TreeNode ConstructCore(int post_start, int post_end, int in_start, int in_end) { char rootValue=_orderDataA[post_end]; TreeNode root = new TreeNode(rootValue); root.LeftSon = root.RightSon =root.Father= null; if (post_start == post_end) { if (in_start == in_end && _orderDataB[in_start] == rootValue) return root; else { Console.WriteLine("数据出错"); throw new Exception(); } } int i = in_start; while (i <= in_end && _orderDataB[i] != rootValue) i++; if(i>in_end) { Console.WriteLine("数据输入出错!请确保输入的是前序和中序序列"); throw new Exception("数据输入出错"); } int leftSonCount = i - in_start; int rightSonCount = in_end - i; if (rightSonCount > 0) { root.RightSon = ConstructCore(post_end-rightSonCount,post_end-1,i+1,in_end); if (root.RightSon != null) root.RightSon.Father = root; } if(leftSonCount>0) { root.LeftSon = ConstructCore(post_start,post_start+leftSonCount-1,in_start,i-1); if (root.LeftSon != null) root.LeftSon.Father = root; } return root; } }
public interface IOrderLaboratory { /// <summary> /// 数据不合法返回null /// </summary> /// <param name="orderDataA">the pre order values</param> /// <param name="orderDataB">the in order values </param> /// <returns></returns> TreeNode PreInConstructor(char[] orderDataA, char[] orderDataB); /// <summary> /// /// </summary> /// <param name="orderDataA">the post order values</param> /// <param name="orderDataB">the in order values </param> /// <returns></returns> TreeNode PostInConstructor(char[] orderDataA, char[] orderDataB); } public class OrderLaboratory : IOrderLaboratory { private char[]preOrderValues; private char[] inOrderValues; private char[] posrOrderValues; IOrderDoctor _IpreAndInDoc; IOrderDoctor _IpostAndInDoc; public OrderLaboratory() { _IpreAndInDoc = new PreAndInOrderDoctor(); _IpostAndInDoc = new PostAndInOrderDoctor(); preOrderValues = null; inOrderValues = null; posrOrderValues = null; } /// <summary> /// can make sure the data is right /// </summary> /// <param name="orderDataA"></param> /// <param name="orderDataB"></param> /// <returns></returns> public TreeNode PreInConstructor(char[] orderDataA, char[] orderDataB) { if (orderDataA == null || orderDataB == null || orderDataA.Length < 1 || orderDataB.Length < 1) { Console.WriteLine("没有数据!!!"); return null; } else if (orderDataA.Length != orderDataB.Length) { Console.WriteLine("数据长短不一致!!!"); return null; } else { try { _IpreAndInDoc.Binding(orderDataA, orderDataB); return _IpreAndInDoc.ConstructTree(); } catch (Exception e) { Console.WriteLine("OrderLaboratory _PreInConstructor_error!!!"); return null; } } } /// <summary> /// can make sure the data is right /// </summary> /// <param name="orderDataA"></param> /// <param name="orderDataB"></param> /// <returns></returns> public TreeNode PostInConstructor(char[] orderDataA, char[] orderDataB) { if (orderDataA == null || orderDataB == null || orderDataA.Length < 1 || orderDataB.Length < 1) { Console.WriteLine("没有数据!!!"); return null; } else if (orderDataA.Length != orderDataB.Length) { Console.WriteLine("数据长短不一致!!!"); return null; } else { try { _IpostAndInDoc.Binding(orderDataA, orderDataB); return _IpostAndInDoc.ConstructTree(); } catch (Exception e) { Console.WriteLine("OrderLaboratory _PreInConstructor_error!!!"); return null; } } } }
困难的是需要测试,大概流程:第一需要测试数据:写了一个类生成树,可以生成满二叉树,全是左子树的二叉树,全是右子树的二叉树,和随机树。第二可以根据树返回相应的前中后序结构,第三构造树,第四检验树的结构。
在检验树这块儿我希望可以打印出树来,所以写了一个打印树的程序。
这个是随机生成的一颗树。
public interface IPrintHelper { void Print(); void Binding(TreeNode _root); } public class ColorPrintHelper:IPrintHelper { private readonly int UNITWIDTH = 5; private readonly int UNITHEIGHT = 5; private readonly int WIDTH = 80; private readonly int HEIGHT = 80; private int maxY; private char[,] charMatrix; public ColorPrintHelper() { maxY = 0; charMatrix = new char[HEIGHT, WIDTH]; RefreshMatrix(); } private void RefreshMatrix() { maxY = 0; for(int i=0;i<HEIGHT;i++) for(int j=0;j<WIDTH;j++) charMatrix[i,j]=' '; } public void Binding(TreeNode _root) { int lc=_root._leftChildrenCount; int rc = _root._rightChildrenCount; int nodeCount = lc + rc+1; if (nodeCount * UNITWIDTH + UNITWIDTH > 80) { Console.WriteLine("树节点太多超出屏幕显示范围"); } else { RefreshMatrix(); MappingTree(lc,rc,_root); } } private void MappingTree(int lc, int rc, TreeNode _root) { int nodeCount = lc + rc + 1; int _root_j = (80 - nodeCount * UNITWIDTH) / 2 + lc * UNITHEIGHT + UNITHEIGHT / 2; int _root_i = 0; charMatrix[_root_i, _root_j] = _root.Value; if (_root.LeftSon != null) MappingLeftSon(_root_i,_root_j,_root.LeftSon); if (_root.RightSon != null) MappingRightSon(_root_i, _root_j, _root.RightSon); } /// <summary> /// /// </summary> /// <param name="fi">father i </param> /// <param name="fj">father j</param> /// <param name="leftNode"></param> private void MappingLeftSon(int fi, int fj, TreeNode node) { int i, j; i=fi+UNITHEIGHT; if (i > maxY) maxY = i ; j=fj-(node._rightChildrenCount + 1) * UNITWIDTH; charMatrix[i, j] = node.Value; //计算太复杂,不用UNITHEIGHT直接补上了 DrawHorizontalLine(j,fj,fi+2); DrawVerticalLine(fi + 1, fi + 1, fj); DrawVerticalLine(fi+3,i-1,j); if(node.LeftSon!=null) { MappingLeftSon(i,j,node.LeftSon); } if (node.RightSon != null) { MappingRightSon(i,j,node.RightSon); } } private void MappingRightSon(int fi, int fj, TreeNode node) { int i, j; i = fi + UNITHEIGHT; if (i > maxY) maxY = i ; j = fj + (node._leftChildrenCount + 1) * UNITWIDTH; charMatrix[i, j] = node.Value; DrawHorizontalLine(fj,j,fi+2); DrawVerticalLine(fi+1,fi+1,fj); DrawVerticalLine(fi + 3, i-1, j); if (node.LeftSon != null) { MappingLeftSon(i, j, node.LeftSon); } if (node.RightSon != null) { MappingRightSon(i, j, node.RightSon); } } private void DrawHorizontalLine(int y1,int y2,int x) { if (y1 > y2) Console.WriteLine("OOP!Error in DrawHorizontalLine"); while (y1 <= y2) { charMatrix[x, y1] = '-'; y1++; } } private void DrawVerticalLine(int x1, int x2, int y) { if (x1 > x2) Console.WriteLine("OOP!Error in DrawVerticalLine"); while (x1 <= x2) { charMatrix[x1, y] = '|'; x1++; } } public void Print() { maxY +=UNITHEIGHT; for(int i=0;i<HEIGHT&&i<maxY;i++) for (int j = 0; j < WIDTH; j++) { Console.Write(charMatrix[i,j]); } } }
原理挺容易的,C#控制台只能打印80个字符,所以树的宽度是 有限制的,每个树节点占5个字符宽度,因此(为了计算方便)最多可以打印15个节点的树,画树时要计算树节点与其儿子节点的距离,A是父亲节点,B是左儿子节点,B有leftCount个子孙节点,不是儿子节点,A与B的距离为:leftCount+1;原理比较简单,考虑清楚之后可以知道A与右儿子的距离。
菜包子
2013年4月19日17:35:45 于马甸桥东