二叉树相关算法是笔试面试常考内容,需重点掌握。
二叉树节点定义:
<span style="font-size:14px;">public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}</span>
常见二叉树笔试题如下
1、二叉树序列化:将二叉树转换成String,空节点用特殊字符代替,一般用‘#’表示,而且String是按每层依次编号,即层次遍历。因为层次遍历的方法也有很多,所以序列化方法也很多,这里采用递归,非递归见4。
二叉树 :3 ,序列化结果为{3,9,20,#,#,15,7}。
/ \
9 20
/ \
15 7
上面的例子在程序中表示:
TreeNode root = new TreeNode(3);
root.left = new TreeNode(9);
root.right = new TreeNode(20);
root.right.left = new TreeNode(15);
root.right.right = new TreeNode(7);
System.out.println(Serialize(root));
序列化程序:
private static StringBuffer sbur = new StringBuffer("");
public static String Serialize(TreeNode root){
if(root==null)
return "";
int i=0;
//nodeAtLevel(root, i)=0说明第i行是空行,所有节点都是空
while(nodeAtLevel(root, i)!=0){
i++;
}
//删除末尾的空格和逗号
i = sbur.length()-1;
while(sbur.charAt(i)==','||sbur.charAt(i)=='#'){
sbur.deleteCharAt(i);
i--;
}
return sbur.toString();
}
//根节点为root的二叉树第level行(从0开始)
private static int nodeAtLevel(TreeNode root, int level){
if(root==null||level <0){
sbur.append("#,");
return 0;
}else if(level==0){
sbur.append(root.val);
sbur.append(",");
return 1;
}else{
return nodeAtLevel(root.left, level-1)+nodeAtLevel(root.right,level-1);
}
}
笔试时也可以采用前序、中序、后序遍历进行序列化,更加简单。
博客http://blog.csdn.net/u012768242/article/details/50990426就是采用的先序遍历。
2、二叉树反序列化:由String重构二叉树,这里String是按每层依次编号。
二叉树第k行有2^(k-1)个节点(k从1开始),前k行共有2^k-1个节点,第k行的第一个元素索引为2^(k-1);
索引为2^(k-1)至2^(k-1)+2^(k-2)-1的节点在第k行左边,索引为2^(k-1)+2^(k-2)至2^k-1的节点在第k行右边;
节点索引为n可推导出行号为k,它是该行第n-(2^(k-1)-1)个节点,与它同一行且位于它左边的节点共有n-2^(k-1)个,这些节点在下一层共有2*n-2^k个子节点,则节点n的左子树节点是k+1行第2*n-2^k+1个节点,右子树节点是k+1行第2*n-2^k+2个节点。
因此,节点n的左子树节点索引为2*n,右子树节点索引为2*n+1。
因此按层次遍历序列化的二叉树,可以按照索引规则进行反序列化。
public static TreeNode Deserialize(String str){
ArrayList<TreeNode> al = new ArrayList<TreeNode>();
int begin = 0;
int n=0;
for(int i=0;i<str.length();i++){
if(str.charAt(i)==','){
String temp = str.substring(begin, i);
if(temp.equals("#"))
al.add(null);
else
al.add(new TreeNode(Integer.parseInt(temp)));
begin = i+1;
n++;
}
}
String temp = str.substring(begin);
al.add(new TreeNode(Integer.parseInt(temp)));
n++;
for(int i=1;i<=n;i++){
if(al.get(i-1)!=null){
if(2*i<=n)
al.get(i-1).left = al.get(2*i-1);
if(2*i+1<=n)
al.get(i-1).right= al.get(2*i);
}
}
return al.get(0);
}
3、数组转化为二叉树
和上面的内容基本一致。以后使用该函数可以很方便的构造二叉树。
博客http://blog.csdn.net/dahai_881222/article/details/7816127则是把一个有序数组转为二叉树存储。
空节点用-1代替,以下图为例。
public static TreeNode arr2Tree(int[] arr){
int n = arr.length;
TreeNode[] tn = new TreeNode[n];
for(int i=0;i<n;i++){
tn[i] = new TreeNode(arr[i]);
}
for(int i=1;i<=n;i++){
if(tn[i-1].val!=-1){
if(2*i<=n)
tn[i-1].left = tn[2*i-1];
if(2*i+1<=n)
tn[i-1].right= tn[2*i];
}
}
return tn[0];
}
4、层次遍历,即二叉树转化为数组
参考:http://blog.csdn.net/zzran/article/details/8778021
http://www.cnblogs.com/miloyip/archive/2010/05/12/binary_tree_traversal.html
http://www.cnblogs.com/kaituorensheng/p/3558645.html
5、前序、中序、后序遍历的递归算法
public static void preOrder(TreeNode root){
if(root==null)
return;
else{
System.out.print(root.val+" ");
preOrder(root.left);
preOrder(root.right);
}
}
public static void midOrder(TreeNode root){
if(root==null)
return;
else{
midOrder(root.left);
System.out.print(root.val+" ");
midOrder(root.right);
}
}
public static void postOrder(TreeNode root){
if(root==null)
return;
else{
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val+" ");
}
6、判断一个树是否为另一颗数的子树
首先判断节点值是否相同,相同则比较各个子节点是否相同,不同则递归判断是否是左子树或右子树的子树。
public static boolean isSubtree(TreeNode root1, TreeNode root2){
if(root2 == null)
return true;
if(root1 == null)
return false;
if(checkNode(root1, root2))
return true;
return isSubtree(root1.left, root2)||isSubtree(root1.right, root2);
}
public static boolean checkNode(TreeNode root1, TreeNode root2){
if(root2 == null)
return true;
if(root1 == null)
return false;
if(root1.val==root2.val)
return checkNode(root1.left,root2.left)&&checkNode(root1.right,root2.right);
else
return false;
}
7、求二叉树的深度
递归算法:二叉树的深度 = max(左子树的深度,右子树的深度)+1。
public static int getDeep(TreeNode root){
if(root==null)
return 0;
int leftdeep = getDeep(root.left);
int rightdeep = getDeep(root.right);
return leftdeep>rightdeep?leftdeep+1:rightdeep+1;
}
8、前序遍历和中序遍历结果重构二叉树(不含重复数字)
前序遍历数组的第一个元素必定是根节点root,在中序遍历数组中找到root,位于根节点左边的一定属于根节点左子树,位于根节点右边的一定属于根节点右子树,然后递归左子树、右子树,将递归结果赋值给root.left和root.right。
public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
int value = pre[0];
TreeNode root = new TreeNode(value);
int i=0;
for(;i<in.length;i++){
if(in[i]==value){
break;
}
}
if(i>0){
int[] inleft = new int[i];
int[] preleft = new int[i];
for(int k=0;k<i;k++){
inleft[k] = in[k];
preleft[k]= pre[k+1];
}
root.left = reConstructBinaryTree(preleft, inleft);
}else{
root.left = null;
}
if(i<in.length-1){
int[] inright= new int[in.length-i-1];
int[] preright= new int[in.length-i-1];
for(int k=0;k<in.length-i-1;k++){
inright[k] = in[k+i+1];
preright[k]= pre[k+i+1];
}
root.right= reConstructBinaryTree(preright,inright);
}else{
root.right = null;
}
return root;
}
9、求二叉树的镜像
public static void Mirror(TreeNode root) {
if(root == null)
return;
TreeNode temp = root.left;
root.left = root.right;
root.right= temp;
if(root.left!=null)
Mirror(root.left);
if(root.right!=null)
Mirror(root.right);
}