入门
TreeNode是经常用到的一个结构体,表示数据结构树(Tree)中的一个节点。其官方定义如下:
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
在Tree的题目中,常会给出一些测试用例,用一些特定的格式来表示一棵树,如[3,9,20,null,null,15,7]就表示如下的一棵树:
特点
确定二叉树
问题1. 同时给定一棵二叉树的先序序列和中序序列,就能唯一确定这棵二叉树? 答案:是
问题2. 同时给定一棵二叉树的中序序列和后序序列,就能唯一确定这棵二叉树? 答案:是
问题3. 同时给定一棵二叉树的先序序列和后序序列,就能唯一确定这棵二叉树? 答案:不是
树的基本结构
package tree;
public class TreeNode<T> {
T value;
TreeNode<T> leftChild;
TreeNode<T> rightChild;
TreeNode(T value) {
this.value = value;
}
TreeNode() {
}
/** 增加左子节点
* addLeft:
* @param value
* void 返回类型
*/
public void addLeft(T value){
TreeNode<T> leftChild = new TreeNode<T>(value);
this.leftChild = leftChild;
}
/**
* addRight: 增加右子节点
* @param value
* void 返回类型
*/
public void addRight(T value){
TreeNode<T> rightChild = new TreeNode<T>(value);
this.rightChild = rightChild;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
* 重载equal方法
*/
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(!(obj instanceof TreeNode)){
return false;
}
return this.value.equals(((TreeNode<?>)obj).value);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
* 重载hashCode方法
*/
@Override
public int hashCode() {
// TODO Auto-generated method stub
return this.value.hashCode();
}
@Override
public String toString(){
return this.value==null?"":this.value.toString();
}
}
树的操作类
package tree;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
* TreeTools:树的操作类
*
* @author xuejupo jpxue@travelsky.com
*
* create in 2015-11-19 下午5:31:05
*
*/
public class TreeTools {
/**
* getTreeNum: 判断树中节点个数
*
* @param root
* 根节点
* @return int 返回类型
*/
public static <T> int getTreeNum(TreeNode<T> root) {
if (root == null) {
return 0;
}
return getTreeNum(root.leftChild) + getTreeNum(root.rightChild) + 1;
}
/**
* getTreeDepth: 判断树的深度
*
* @param root
* 根节点
* @return int 返回类型
*/
public static <T> int getTreeDepth(TreeNode<T> root) {
if (root == null) {
return 0;
}
int leftDepth = getTreeDepth(root.leftChild) + 1;
int rightDepth = getTreeDepth(root.rightChild) + 1;
return Math.max(leftDepth, rightDepth);
}
/**
* preOrderTravel: 前序遍历
*
* @param root
* void 返回类型
*/
public static <T> void preOrderTravel(TreeNode<T> root) {
if (root == null) {
return;
}
visitNode(root);
preOrderTravel(root.leftChild);
preOrderTravel(root.rightChild);
}
/**
* midOrderTravel: 中序遍历
*
* @param root
* void 返回类型
*/
public static <T> void midOrderTravel(TreeNode<T> root) {
if (root == null) {
return;
}
midOrderTravel(root.leftChild);
visitNode(root);
midOrderTravel(root.rightChild);
}
/**
* backOrderTravel: 后序遍历
*
* @param root
* void 返回类型
*/
public static <T> void backOrderTravel(TreeNode<T> root) {
if (root == null) {
return;
}
backOrderTravel(root.leftChild);
backOrderTravel(root.rightChild);
visitNode(root);
}
/**
* visitNode: 访问node节点
*
* @param node
* void 返回类型
*/
private static <T> void visitNode(TreeNode<T> node) {
System.out.print(node.value + "\t");
}
/**
* levelTravel: 分层遍历
*
* @param root
* void 返回类型
*/
public static <T> void levelTravel(TreeNode<T> root) {
Queue<TreeNode<T>> q = new LinkedList<TreeNode<T>>();
q.offer(root);
while (!q.isEmpty()) {
TreeNode<T> temp = q.poll();
visitNode(temp);
if (temp.leftChild != null) {
q.offer(temp.leftChild);
}
if (temp.rightChild != null) {
q.offer(temp.rightChild);
}
}
}
/**
* getNumForKlevel: 求第K层节点个数
*
* @param root
* @param k
* @return int 返回类型
*/
public static <T> int getNumForKlevel(TreeNode<T> root, int k) {
if (root == null || k < 1) {
return 0;
}
if (k == 1) {
return 1;
}
int leftNum = getNumForKlevel(root.leftChild, k - 1);
int rightNum = getNumForKlevel(root.rightChild, k - 1);
return leftNum + rightNum;
}
/**
* getLeafNum: 求二叉树中叶子节点的个数
*
* @param root
* @return int 返回类型
*/
public static <T> int getLeafNum(TreeNode<T> root) {
if (root == null) {
return 0;
}
if (root.leftChild == null && root.rightChild == null) {
return 1;
}
int leftNum = getLeafNum(root.leftChild);
int rightNum = getLeafNum(root.rightChild);
return leftNum + rightNum;
}
/**
* exchange: 交换根节点的左右子树
*
* @param root
* @return TreeNode 返回类型
*/
public static <T> TreeNode<T> exchange(TreeNode<T> root) {
if (root == null) {
return null;
}
TreeNode<T> left = exchange(root.leftChild);
TreeNode<T> right = exchange(root.rightChild);
root.leftChild = right;
root.rightChild = left;
return root;
}
/**
* nodeIsChild: 查看node是否是root的子节点
*
* @param root
* @param node
* @return boolean 返回类型
*/
public static <T> boolean nodeIsChild(TreeNode<T> root, TreeNode<T> node) {
if (root == null || node == null) {
return false;
}
if (root == node) {
return true;
}
boolean isFind = nodeIsChild(root.leftChild, node);
if (!isFind) {
isFind = nodeIsChild(root.rightChild, node);
}
return isFind;
}
/**
* findAllFatherNode: 返回两个节点lnode和rnode的以root为根节点的公共父节点
*
* @param root
* 根节点
* @param lNode
* @param rNode
* @return TreeNode 返回类型
*/
public static <T> TreeNode<T> findAllFatherNode(TreeNode<T> root,
TreeNode<T> lNode, TreeNode<T> rNode) {
if (lNode == root || rNode == root) {
return root;
}
if (root == null || lNode == null || rNode == null) {
return null;
}
// 如果lNode是左子树的节点
if (nodeIsChild(root.leftChild, lNode)) {
if (nodeIsChild(root.rightChild, rNode)) {
return root;
} else {
return findAllFatherNode(root.leftChild, lNode, rNode);
}
} else {
if (nodeIsChild(root.leftChild, rNode)) {
return root;
} else {
return findAllFatherNode(root.rightChild, lNode, rNode);
}
}
}
/**
* getTreeFromPreAndMid: 根据前序和中序构建二叉树
*
* @param pre
* 前序序列
* @param mid
* 中序序列
* @return TreeNode 返回类型
*/
public static <T> TreeNode<T> getTreeFromPreAndMid(List<T> pre, List<T> mid) {
if (pre == null || mid == null || pre.size() == 0 || mid.size() == 0) {
return null;
}
if (pre.size() == 1) {
return new TreeNode<T>(pre.get(0));
}
TreeNode<T> root = new TreeNode<T>(pre.get(0));
// 找出根节点在中序中的位置
int index = 0;
while (!mid.get(index++).equals(pre.get(0))) {
}
// 构建左子树的前序
List<T> preLeft = new ArrayList<T>(index);
// 左子树的中序
List<T> midLeft = new ArrayList<T>(index);
for (int i = 1; i < index; i++) {
preLeft.add(pre.get(i));
}
for (int i = 0; i < index - 1; i++) {
midLeft.add(mid.get(i));
}
// 重建左子树
root.leftChild = getTreeFromPreAndMid(preLeft, midLeft);
// 右子树的前序
List<T> preRight = new ArrayList<T>(pre.size() - index - 1);
// 右子树的中序
List<T> midRight = new ArrayList<T>(pre.size() - index - 1);
for (int i = 0; i <= pre.size() - index - 1; i++) {
preRight.add(pre.get(index + i));
}
for (int i = 0; i <= pre.size() - index - 1; i++) {
midRight.add(mid.get(index + i));
}
// 重建→子树
root.rightChild = getTreeFromPreAndMid(preRight, midRight);
return root;
}
/**
* equals: 查看node1和node2两棵树是否相等(两棵树所有节点都相等)
*
* @param node1
* node2 两个节点
* @return boolean 返回类型
*/
public static <T> boolean equals(TreeNode<T> node1, TreeNode<T> node2) {
// TODO Auto-generated method stub
if (node1 == null && node2 == null) {
return true;
} else if (node1 == null || node2 == null) {
return false;
}
boolean isEqual = node1.value.equals(node2.value);
boolean isLeftEqual = equals(node1.leftChild, node2.leftChild);
boolean isRightEqual = equals(node1.rightChild, node2.rightChild);
return isEqual && isLeftEqual && isRightEqual;
}
}
测试类
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeNode<Integer> t = new TreeNode<Integer>(1);
t.addLeft(2);
t.addRight(3);
t.leftChild.addLeft(4);
t.leftChild.addRight(5);
System.out.println("中序遍历测试:");
TreeTools.midOrderTravel(t);
System.out.println("\n前序遍历测试:");
TreeTools.preOrderTravel(t);
System.out.println("\n后序遍历测试:");
TreeTools.backOrderTravel(t);
System.out.println("\n层次遍历测试:");
TreeTools.levelTravel(t);
System.out.println("\n树的深度:"+TreeTools.getTreeDepth(t));
System.out.println("树的叶子个数:"+TreeTools.getLeafNum(t));
System.out.println("树的节点个数:"+TreeTools.getTreeNum(t));
System.out.println("第2层节点个数为:"+TreeTools.getNumForKlevel(t,2));
List<Integer> pre = new ArrayList<Integer>();
pre.add(1);
pre.add(2);
pre.add(4);
pre.add(5);
pre.add(3);
List<Integer> mid = new ArrayList<Integer>();
mid.add(4);
mid.add(2);
mid.add(5);
mid.add(1);
mid.add(3);
TreeNode<Integer> root = TreeTools.getTreeFromPreAndMid(pre, mid);
System.out.println("\n通过前序和中序构建树测试:");
TreeTools.levelTravel(root);
System.out.println("\n构建的树比较测试:");
System.out.println(TreeTools.equals(t,root));
}
结果:
-----------------------------------------------
中序遍历测试:
4 2 5 1 3
前序遍历测试:
1 2 4 5 3
后序遍历测试:
4 5 2 3 1
层次遍历测试:
1 2 3 4 5
树的深度:3
树的叶子个数:3
树的节点个数:5
第2层节点个数为:2
通过前序和中序构建树测试:
1 2 3 4 5
构建的树比较测试:
true
-----------------------------------------------
经典例题
以层次结构输入一颗树
输入为整数n,层次遍历向二叉树中填入1、2、3…n-1、n,形成一颗二叉树。
参考链接:
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int lastCount = 0;
while(scanner.hasNext()){
int n = scanner.nextInt();
if (n == 0){
break;
}
Tree t = MakeTree(1, n);//树构造成功
}
}
class Tree{//树节点结构
private int val;
private Tree leftleaf;
private Tree rightleaf;
public Tree(int val){
this.val = val;
this.leftleaf = null;
this.rightleaf = null;
}
public int getVal(){
return this.val;
}
public Tree getLeftleaf(){
return this.leftleaf;
}
public Tree getRightleaf(){
return this.rightleaf;
}
public void setVal(int val) {
this.val = val;
}
public void setLeftleaf(Tree leftleaf) {
this.leftleaf = leftleaf;
}
public void setRightleaf(Tree rightleaf) {
this.rightleaf = rightleaf;
}
}
层次遍历造树
public static Tree MakeTree(int i,int n){
if (n == 0)
return null;
else if (i <= n){
Tree root = new Main().new Tree(i);
System.out.println(root.getVal());
root.setLeftleaf(MakeTree(i*2,n));
root.setLeftleaf(MakeTree(i*2+1,n));
return root;
}else{
return null;
}
}
}
FBI树
我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。
FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:
1)T的根结点为R,其类型与串S的类型相同;
2)若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2。
现在给定一个长度为2N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。
数据规模和约定
对于全部的数据,N < = 10。
注:
[1] 二叉树:二叉树是结点的有限集合,这个集合或为空集,或由一个根结点和两棵不相交的二叉树组成。这两棵不相交的二叉树分别称为这个根结点的左子树和右子树。
[2] 后序遍历:后序遍历是深度优先遍历二叉树的一种方法,它的递归定义是:先后序遍历左子树,再后序遍历右子树,最后访问根。
输入
第一行是一个整数N(0 < = N < = 10),第二行是一个长度为2N的“01”串。
输出
包括一行,这一行只包含一个字符串,即FBI树的后序遍历序列。
样例输入
3
10001011
样例输出
IBFBBBFIBFIIIFF
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
static String str = "";
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int n1 = 1 << n;//左移等于1*2的n次方,右移等于-/(2的n次方)
//System.out.println(n);
String s = scanner.next();
//System.out.println(s);
Main.FBITree t = new Main().new FBITree(s,n1);//调用内部类的构造函数,通过外部类对象调用
houxu(t.root);
System.out.println(str);
}
class Tree{//树结构
private String val;
private Tree leftleaf;
private Tree rightleaf;
public Tree(String val){
this.val = val;
this.leftleaf = null;
this.rightleaf = null;
}
public String getVal(){
return this.val;
}
public Tree getLeftleaf(){
return this.leftleaf;
}
public Tree getRightleaf(){
return this.rightleaf;
}
public void setVal(String val) {
this.val = val;
}
public void setLeftleaf(Tree leftleaf) {
this.leftleaf = leftleaf;
}
public void setRightleaf(Tree rightleaf) {
this.rightleaf = rightleaf;
}
}
public static void houxu(Tree t){//树的后续遍历
if (t != null){
houxu(t.leftleaf);
houxu(t.rightleaf);
str = str + t.getVal();
}
}
//注意:这是一个内部类在调用该类时需通过外部类对象来调用内部类
class FBITree{//根据题目要求建树过程
Tree root;
public FBITree(String str,int count){
if(count>0) {
if(str.indexOf("0")!=-1&&str.indexOf("1")!=-1)//str中间即存在0又存在1
root=new Tree("F");
else if(str.indexOf("0")!=-1&&str.indexOf("1")==-1)//str中只存在0
root=new Tree("B");
else
root=new Tree("I");//str中只存在1
String lStr=str.substring(0,str.length()/2);
String rStr=str.substring(str.length()/2,str.length());
root.setLeftleaf(new FBITree(lStr,str.length()/2).root);
root.setRightleaf(new FBITree(rStr,str.length()/2).root);
}
}
}
}