面試準備系列03----面試中的二叉樹題目彙總
本文是面試準備系列的第3篇,二叉樹和鏈表一樣,首先都應該想到遞歸。所以本文中儘量都用遞歸和非遞歸完成每一題。前一篇爲《面試準備系列03----面試中的二叉樹題目彙總》
1.二叉樹的遍歷,前序中序後序,遞歸和非遞歸
http://blog.csdn.net/sheepmu/article/details/28941285
2.二叉樹的層序遍歷
3.二叉樹的高度/最小高度
4.二叉樹的節點個數
5.求二叉樹的鏡像
6.判斷兩顆二叉樹是否互爲鏡像
7.判斷一棵樹是否本身就是鏡像樹
8.判斷兩顆二叉樹是不是相同的樹
9.判斷樹1是不是樹2的子結構
10.判斷二叉樹是否是平衡二叉樹
11.二叉樹第k層的節點個數
12.二叉樹葉子節點的個數
13.由前序遍歷和中序遍歷重構二叉樹
14.由中序遍歷和後序遍歷重構二叉樹
15.二叉樹中兩節點的最大距離
16.二叉樹中和爲某一值的路徑17.求二叉樹中兩個節點的最低公共祖先節點
package com.sheepmu;
import java.util.LinkedList;
import java.util.Stack;
class TreeNode
{
String value;
TreeNode left;
TreeNode right;
public TreeNode(String value )
{
this.value = value;
}
}
public class BinaryTree
{
//2.二叉樹的層序遍歷
//思路:利用隊列實現二叉樹的層序遍歷。
public void cx(TreeNode root)
{
if(root==null)
return;
LinkedList<TreeNode> queue=new LinkedList<TreeNode>();
queue.addLast(root);
while(!queue.isEmpty())
{
TreeNode cur=queue.removeFirst();
System.out.print(cur.value+" ");
if(cur.left!=null)
queue.addLast(cur.left);
if(cur.right!=null)
queue.addLast(cur.right);
}
}
//3.二叉樹的高度 --遞歸--
public int getHighRec(TreeNode root)
{
if(root==null)
return 0;
return Math.max(getHighRec(root.left), getHighRec(root.right))+1;
}
//3.二叉樹的高度 --非 遞歸--
//思路:層序遍歷,對當前層和下一層的節點數計數。
public int getHigh(TreeNode root)
{
if(root==null)
return 0;
LinkedList<TreeNode> queue=new LinkedList<TreeNode>();
queue.addLast(root);
int high=0;
int curLevelNodes=1,nextLevelNodes=0;
while(!queue.isEmpty())
{
TreeNode cur=queue.removeFirst();
curLevelNodes--;
if(cur.left!=null)
{
queue.addLast(cur.left);
nextLevelNodes++;
}
if(cur.right!=null)
{
queue.addLast(cur.right);
nextLevelNodes++;
}
if(curLevelNodes==0)
{
high++;
curLevelNodes=nextLevelNodes;
nextLevelNodes=0;
}
}
return high;
}
//求二叉樹的最低高度,若有左or右子樹爲null的情況,則最小高度是另一非null子樹的最小高度 --遞歸--
public int getMinHigh(TreeNode root)
{
if(root==null)
return 0;
if(root.left==null&&root.right==null)
return 1;
if(root.left==null)
return 1+getMinHigh(root.right);
if(root.right==null)
return 1+getMinHigh(root.left);
return 1+Math.min(getMinHigh(root.right),getMinHigh(root.left)) ;
}
//4.二叉樹的節點個數 --遞歸--
public int getNodesNumRec(TreeNode root)
{
if(root==null)
return 0;
return getNodesNumRec(root.left)+getNodesNumRec( root.right)+1;
}
//4.二叉樹的節點個數 --遞歸--
//思路:層序遍歷記錄個數
public int getNodesNum(TreeNode root)
{
if(root==null)
return 0;
LinkedList<TreeNode> queue=new LinkedList<TreeNode>();
queue.addLast(root);
int num=1;
while(!queue.isEmpty())
{
TreeNode cur=queue.removeFirst();
if(cur.left!=null)
{
queue.addLast(cur.left);
num++;
}
if(cur.right!=null)
{
queue.addLast(cur.right);
num++;
}
}
return num;
}
//5.求二叉樹的鏡像(直接把原樹變爲其鏡像樹,即破壞原樹) --遞歸--
//思路:把原樹的左子樹置爲其右子樹的鏡像;把原樹的右子樹置爲其左子樹的鏡像
public TreeNode getJXRec(TreeNode root)
{
if(root==null)
return null;
TreeNode tleft=getJXRec(root.right);
TreeNode tright=getJXRec(root.left);
root.left=tleft;
root.right=tright;
return root;
}
//5.求二叉樹的鏡像(直接把原樹變爲其鏡像樹,即破壞原樹) --非遞歸--
//思路: 利用Stsck,讓節點的子節點互相交換
public TreeNode getJX(TreeNode root)
{
if(root==null)
return null;
Stack<TreeNode> stack=new Stack<TreeNode>();
stack.push(root);
while(!stack.isEmpty())
{
TreeNode cur=stack.pop();
TreeNode temp=cur.right;
cur.right=cur.left;
cur.left=temp;
if(cur.right!=null)
stack.push(cur.right);
if(cur.left!=null)
stack.push(cur.left);
}
return root;
}
//5.求二叉樹的鏡像(生成一顆新樹,即不改變原樹結構) --遞歸--
public TreeNode newJXRec(TreeNode root)
{
if(root==null)
return null;
TreeNode newTree=new TreeNode (root.value);
newTree.left=newJXRec(root.right);
newTree.right=newJXRec(root.left);
return newTree;
}
//5.求二叉樹的鏡像(生成一顆新樹,即不改變原樹結構) --非 遞歸--
//6.判斷兩個二叉樹是否互爲鏡像樹 --遞歸--
public boolean isJXRec(TreeNode root1,TreeNode root2)
{
if(root1==null&&root2==null)
return true;
if(root1==null||root2==null)
return false;
if(root1.value!=root2.value)
return false;
return isJXRec( root1.left, root2.right)&&isJXRec( root1.right, root2.left);
}
//7.判斷一顆二叉樹本身是否爲鏡像樹 --遞歸--
public boolean isJXRec2(TreeNode root)
{
if(root==null)
return true;
return isJXRec2(root.left)&&isJXRec2(root.right);
}
//8.判斷兩顆二叉樹是不是相同的樹 --遞歸--
public boolean isSameTreeRec(TreeNode root1,TreeNode root2)
{
if(root1==null&&root2==null)
return true;
if(root1==null||root2==null)
return false;
if(root1.value!=root2.value)
return false;
return isSameTreeRec( root1.left,root2.left)&&isSameTreeRec(root1.right,root2.right);
}
//8.判斷兩顆二叉樹是不是相同的樹 --非遞歸--
public boolean isSameTree(TreeNode root1,TreeNode root2)
{
if(root1==null&&root2==null)
return true;
if(root1==null||root2==null)
return false;
if(root1.value!=root2.value)
return false;
Stack<TreeNode> stack1=new Stack<TreeNode>();
Stack<TreeNode> stack2=new Stack<TreeNode>();
stack1.push(root1);
stack2.push(root2);
while(!stack1.isEmpty()&&!stack1.isEmpty())
{
TreeNode cur1=stack1.pop();
TreeNode cur2=stack2.pop();
if(cur1.value!=cur2.value)
return false;
else
{
if(cur1.right!=null&&cur2.right!=null)
{
stack1.push(cur1.right);
stack2.push(cur2.right);
}
if(cur1.left!=null&&cur2.left!=null)
{
stack1.push(cur1.left);
stack2.push(cur2.left);
}
if(cur1.left==null&&cur1.right==null&&cur2.left==null&&cur2.right==null)
return true;
else
return false;
}
}
return true;
}
//9.判斷二叉樹1是不是二叉樹2的子結構
//10.判斷二叉樹是否是平衡二叉樹 --遞歸-- 但是這種方式雖然簡潔,但是每個節點會被遍歷多次,並不高效
public boolean isBlanced(TreeNode root)
{
if(root==null)
return true;
if(Math.abs(getHighRec(root.left)-getHighRec(root.right))>1)//先判斷整個左右子樹高度差
return false;
return isBlanced(root.left)&&isBlanced( root.right);
}
//10.判斷二叉樹是否是平衡二叉樹 --遞歸-- 更加高效的解法:每個節點只被遍歷一次
//11.在遍歷的過程中一邊遍歷一邊計算高度
public boolean isBlanced2(TreeNode root)
{
int high=0;
return isBlanced3(root,high);
}
public boolean isBlanced3(TreeNode root,int high)
{
if(root==null)
{
high=0;
return true;
}
int lefthigh=0,righthigh=0;
if(isBlanced3(root.left,lefthigh)&&isBlanced3(root.right,righthigh))
{
if(Math.abs(lefthigh-righthigh)<=1)
{
high=1+Math.max(lefthigh, righthigh);
return true;
}
}
return false;
}
//11.求二叉樹第k層的節點個數 --遞歸--
public int getNodesInKRec(TreeNode root,int k)
{
if(root==null||k<1)
return 0;
if( k==1)
return 1;
return getNodesInKRec(root.left,k-1)+getNodesInKRec(root.right,k-1);
}
//11.求二叉樹第k層的節點個數 --非 遞歸--
//思路:層序遍歷,類似於非遞歸求高度
public int getNodesInK(TreeNode root,int k)
{
if(root==null||k<1)
return 0;
if(k==1)
return 1;
LinkedList<TreeNode> queue=new LinkedList<TreeNode>();
queue.addLast(root);
int curLevelNodes=1;
int nextLevelNodes=0;
int high=1;
while(!queue.isEmpty()&&high<k)
{
TreeNode cur=queue.removeFirst();
curLevelNodes--;
if(cur.left!=null)
{
queue.addLast(root.left);
nextLevelNodes++;
}
if(cur.right!=null)
{
queue.addLast(root.right);
nextLevelNodes++;
}
if(curLevelNodes==0)
{
high++;
curLevelNodes=nextLevelNodes;
nextLevelNodes=0;
}
}
return curLevelNodes;
}
//12.求二叉樹的葉子節點數 --遞歸--
public int getYeNodesRec(TreeNode root)
{
if(root==null)
return 0;
if(root.left==null&&root.right==null)
return 1;
return getYeNodesRec(root.left)+getYeNodesRec( root.right);
}
//12.求二叉樹的葉子節點數 --非遞歸--
public int getYeNodes(TreeNode root)
{
if(root==null)
return 0;
if(root.left==null&&root.right==null)
return 1;
Stack<TreeNode> stack=new Stack<TreeNode>();
stack.push(root);
int num=0;
while(!stack.isEmpty())
{
TreeNode cur=stack.pop();
if(cur.right!=null)
{
stack.push(cur.right);
}
if(cur.left!=null)
{
stack.push(cur.left);
}
if(cur.right==null&&cur.left==null)
num++;
}
return num;
}
//13.由前序遍歷和中序遍歷重構二叉樹 --遞歸--
//先找到根節點,在分別找到左右子樹的前序和中序,遞歸
public TreeNode buildTreeRec(String pre,String mid)
{
if(pre==null||mid==null)
return null;
if(pre.length()==0||mid.length()==0)
return null;
if(pre.length()!=mid.length())
return null;
int len=pre.length();
TreeNode root=new TreeNode(pre.charAt(0)+"");//先找到根節點,前序遍歷的第一個是根節點
int i=0;
while(mid.charAt(i)!=pre.charAt(0))//找到中序遍歷中根節點的位置,那麼它前面的即是左樹,後面的是右樹
i++;
root.left= buildTreeRec(pre.substring(1, 1+i) ,mid.substring(0, i) );//由左樹的前序和後序構造新左樹
root.right= buildTreeRec(pre.substring(i+1, len) ,mid.substring(i+1, len) );//由右樹的前序和後序構造新的右樹
return root;
}
//14.由中序遍歷和後序遍歷重構二叉樹 --遞歸--
//先找到根節點,在分別找到左右子樹的中序和後序,遞歸
public TreeNode buildTreeRec2(String mid,String pro)
{
if(mid==null||pro==null)
return null;
if(mid.length()==0||pro.length()==0)
return null;
if(mid.length()!=pro.length())
return null;
int len=mid.length();
TreeNode root=new TreeNode(pro.charAt(len-1)+"");//後序的最後一個是根節點
int i=0;
while(mid.charAt(i)!=pro.charAt(len-1))//找到中序遍歷中根節點的位置,那麼它前面的即是左樹,後面的是右樹
i++;
root.left=buildTreeRec2( mid.substring(0, i),pro.substring(0, i) );
root.right=buildTreeRec2( mid.substring(i+1, len),pro.substring(i, len-1) );
return root;
}
//15.二叉樹中兩節點的最大距離
/**
* 計算一個二叉樹的最大距離有兩個情況:
情況A: 路徑經過左子樹的最深節點,通過根節點,再到右子樹的最深節點。
情況B: 路徑不穿過根節點,而是左子樹或右子樹的最大距離路徑,取其大者。
只需要計算這兩個情況的路徑距離,並取其大者,就是該二叉樹的最大距離
*/
public static Result getMaxDistanceRec(TreeNode root)
{
if(root == null)
{
Result empty = new Result(0, -1); // 目的是讓調用方 +1 後,把當前的不存在的 (NULL) 子樹當成最大深度爲 0
return empty;
}
// 計算出左右子樹分別最大距離
Result lmd = getMaxDistanceRec(root.left);
Result rmd = getMaxDistanceRec(root.right);
Result res = new Result();
res.maxDepth = Math.max(lmd.maxDepth, rmd.maxDepth) + 1; // 當前最大深度
// 取情況A和情況B中較大值
res.maxDistance = Math.max( lmd.maxDepth+rmd.maxDepth, Math.max(lmd.maxDistance, rmd.maxDistance) );
return res;
}
private static class Result
{
int maxDistance;
int maxDepth;
public Result()
{
}
public Result(int maxDistance, int maxDepth)
{
this.maxDistance = maxDistance;
this.maxDepth = maxDepth;
}
}
//16.最低公共祖先節點
public TreeNode getLastCommonParentRec(TreeNode root, TreeNode n1, TreeNode n2)
{
if(root == null)
return null;
// 如果有一個match,則說明當前node就是要找的最低公共祖先 注意遞歸條件!!!!!
if(root.equals(n1) || root.equals(n2))
return root;
TreeNode commonInLeft = getLastCommonParentRec(root.left, n1, n2);
TreeNode commonInRight = getLastCommonParentRec(root.right, n1, n2);
// 如果一個左子樹找到,一個在右子樹找到,則說明root是唯一可能的最低公共祖先
if(commonInLeft!=null && commonInRight!=null)
return root;
// 其他情況是要不然在左子樹要不然在右子樹
if(commonInLeft != null)
return commonInLeft;
return commonInRight;
}
/*
A
/ \
B C
/ \ \
D E F
*/
public static void main(String[] args)
{
TreeNode n1=new TreeNode("A");
TreeNode n2=new TreeNode("B");
TreeNode n3=new TreeNode("C");
TreeNode n4=new TreeNode("D");
TreeNode n5=new TreeNode("E");
TreeNode n6=new TreeNode("F");
n1.left=n2;
n1.right=n3;
n2.left=n4;
n2.right=n5;
n3.right=n6;
TreeNode root=n1;
BinaryTree bt=new BinaryTree();
System.out.print("層序遍歷---->" );
bt.cx(root);
System.out.print("\n");
System.out.println("遞歸高度---->"+bt.getHighRec(root));
System.out.println("非遞歸高度---->"+bt.getHigh(root));
System.out.println("遞歸節點個數---->"+bt.getNodesNumRec(root));
System.out.println("非遞歸節點個數---->"+bt.getNodesNum(root));
// bt.getJXRec(root);
// bt.cx(root);
// System.out.print("\n");
// System.out.print("把樹變爲本身的鏡像樹後層序遍歷---->" );
// bt.getJX(root);
// bt.cx(root);
System.out.println("是否是平衡二叉樹---->"+bt.isBlanced(root));
System.out.println("是否是平衡二叉樹,每個節點只遍歷一次的方法---->"+bt.isBlanced2(root));
System.out.println("遞歸第 k層節點個數---->"+bt.getNodesInKRec(root, 3));
System.out.println("非遞歸第 k層節點個數---->"+bt.getNodesInK(root, 3));
System.out.println("遞歸葉節點個數---->"+bt.getYeNodesRec(root));
System.out.println("非遞歸葉節點個數---->"+bt.getYeNodes(root));
System.out.print("由前序遍歷和中序遍歷構造的樹的 層序遍歷---->" );
bt.cx(bt.buildTreeRec("ABDECF", "DBEACF"));
System.out.print("\n" );
System.out.print("由中序遍歷和後序遍歷構造的樹的 層序遍歷---->" );
bt.cx(bt.buildTreeRec2("DBEACF", "DEBFCA"));
System.out.print("\n" );
System.out.println("最低公共祖先---->"+bt.getLastCommonParentRec(root,n4,n3).value);
}
}
層序遍歷---->A B C D E F 遞歸高度---->3
非遞歸高度---->3
遞歸節點個數---->6
非遞歸節點個數---->6
是否是平衡二叉樹---->true
是否是平衡二叉樹,每個節點只遍歷一次的方法---->true
遞歸第 k層節點個數---->3
非遞歸第 k層節點個數---->3
遞歸葉節點個數---->3
非遞歸葉節點個數---->3
由前序遍歷和中序遍歷構造的樹的 層序遍歷---->A B C D E F
由中序遍歷和後序遍歷構造的樹的 層序遍歷---->A B C D E F
最低公共祖先---->A