02. 树的遍历算法(递归和非递归实现)
树从存储方式上可分为顺序树和链式树
这节我们主要说的是链式树。
1. 定义结点
public class Node {
public String name; //结点的数据域,拿String name代替
public Node lchild; //存放左孩子
public Node rchild; //存放右孩子
public boolean status; //非递归后序遍历会用到
public Node() {
// TODO Auto-generated constructor stub
this.name = "";
this.lchild = null;
this.rchild = null;
this.status = false;
}
public Node(String string) {
// TODO Auto-generated constructor stub
this.name = string;
this.lchild = null;
this.rchild = null;
this.status = false;
}
}
2. 树的创建
定义一个Tree类,里面主要实现树的创建,和一些遍历操作,先部分展示代码并讲解,在最后会给出全部源码。
我们为了养成良好的习惯,建议把每个东西归结成一个类,比如树,栈,队列,都分开定义,到用的时候,直接new就行了。
先定义数据结构
1.树的结构:
public class Tree {
// 声明输入流
Scanner scanner;
// 声明一个队列(层序遍历会用到)
Seqqueue queue;
// 声明一个栈(非递归的先序,中序,后序会用到)
Seqstack stack;
public Tree() {
// TODO Auto-generated constructor stub
// 初始化输入流
this.scanner = new Scanner(System.in);
// 初始化队列
this.queue = new Seqqueue();
this.queue.initQueue(10);
// 初始化栈
this.stack = new Seqstack();
this.stack.InitStack(20);
}
2.栈的结构:(树的非递归遍历会用到)
public class Seqstack {
// 节点数组(模拟顺序栈)
public Node[] nodes;
// 栈顶下标
public int top;
public Seqstack() {
// TODO Auto-generated constructor stub
this.top = -1;
}
// 初始化栈
public void InitStack(int size) {
this.nodes = new Node[size];
this.top = -1;
}
// 判断栈空
public boolean IsEmpty() {
if (this.top == -1) {
return true;
}
return false;
}
// 判断栈满
public boolean IsFull() {
if (this.top == this.nodes.length - 1) {
return true;
}
return false;
}
// 入栈
public void push(Node node) {
if (IsFull()) {
System.out.println("栈已经满了,入栈失败");
return;
}
this.top++;
this.nodes[this.top] = node;
}
// 出栈
public Node pop() {
if (IsEmpty()) {
System.out.println("栈已经空了,出栈失败");
return null;
}
Node node = this.nodes[this.top];
this.top--;
return node;
}
// 清空栈
public void ClearStack() {
// 如果不为空,就一直出栈
while (!IsEmpty()) {
Node node = pop();
}
}
// 拿到栈顶元素
public Node getTop() {
if (IsEmpty()) {
System.out.println("栈中没有元素");
return null;
}
return this.nodes[this.top];
}
}
3.循环队列的结构:(树的层序遍历会用到)
public class Seqqueue {
public Node[] queue;
public int QUEUE_SIZE;
public int front;
public int rear;
//初始化队列
public void initQueue(int size) {
this.QUEUE_SIZE = size;
this.queue = new Node[size];
this.front = 0;
this.rear = 0;
}
//判断队列是否为空
public boolean isEmpty() {
if(this.front == this.rear ) {
return true;
}
else {
return false;
}
}
//判断队列是否为满
public boolean isFull() {
if((this.front % this.QUEUE_SIZE) == ((this.rear+1) % this.QUEUE_SIZE)) {
return true;
}
else {
return false;
}
}
//入队
public void enterQueue(Node node) {
if(isFull()) {
System.out.println("队列已经满了,入队失败");
return;
}
this.queue[this.rear] = node;
this.rear ++;
this.rear = this.rear % this.QUEUE_SIZE;
}
//出队
public Node deleteQueue() {
if(isEmpty()){
System.out.println("队列为空,出队失败");
return null;
}
this.front = this.front % this.QUEUE_SIZE;
Node node = this.queue[this.front];
this.front++;
return node;
}
}
- 树的递归创建方式:(使用先序创建)
// 递归创建树(树的头节点)
Node createBiTree(Node tree) {
String string = scanner.nextLine();
if (string.equals(".")) {
return null;
}
tree = new Node();
tree.name = string;
tree.lchild = createBiTree(tree.lchild);
tree.rchild = createBiTree(tree.rchild);
return tree;
}
3. 树的递归遍历
我们利用三种遍历的特点,根据他们的打印顺序总结出来:
先序:根左右
中序:左跟右
后序:左右跟
3.1 树的递归先序遍历
// 先序遍历(递归)
void PreOrder(Node tree) {
if (tree != null) {
System.out.println(tree.name);
PreOrder(tree.lchild);
PreOrder(tree.rchild);
}
}
3.2 树的递归中序遍历
// 中序遍历(递归)
void Inthread(Node tree) {
if (tree != null) {
Inthread(tree.lchild);
System.out.println(tree.name);
Inthread(tree.rchild);
}
}
3.3 树的递归后序遍历
// 后序遍历(递归)
void PostOrder(Node tree) {
if (tree != null) {
PostOrder(tree.lchild);
PostOrder(tree.rchild);
System.out.println(tree.name);
}
}
递归的遍历特别简单,只需要知道递归的原理就OK了,不做详解。
4. 树的非递归遍历
树的非递归遍历,还会用到栈,利用栈的先进后出的特点,实现树的遍历。
4.1 树的非递归先序遍历
这块我会给出两种方法,原理基本相同。
(1)根据先序打印特点:根左右。
我们用到压栈和出栈来模拟这个过程:
- 由于栈是先进后出的,所以我们先访问根节点,压栈。
- 然后判断栈不为空,然后出栈,打印。
- 对出栈的结点进行判断,他有没有右孩子,如果有,把右孩子压栈。
- 再判断有没有左孩子,如果有,把左孩子压栈。
- 完成后,再跳回第2步,一直循环
// 先序遍历(非递归)
void nonPreOrder(Node tree) {
Node temp = null;
if (tree == null) {
System.out.println("树不存在");
return;
}
stack.push(tree);
while (!stack.IsEmpty()) {
temp = stack.pop();
System.out.println(temp.name);
if (temp.rchild != null) {
stack.push(temp.rchild);
}
if (temp.lchild != null) {
stack.push(temp.lchild);
}
}
}
(2)第二种只是把压栈的顺序,进行了一点修正,其他的都差不多,但是,为了方便记忆非递归的先序,中序,后序,建议学习第二种的算法。
// 先序遍历(非递归)
void nonPreOrder2(Node tree) {
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp = null;
temp = tree;
while (!stack.IsEmpty() || temp != null) {
while (temp != null) {
System.out.println(temp.name);
stack.push(temp);
temp = temp.lchild;
}
temp = stack.pop();
temp = temp.rchild;
}
}
4.2 树的非递归中序遍历
根据中序打印特点:左跟右。
我们用到压栈和出栈来模拟这个过程:
- 由于栈是先进后出的,所以我们先访问根节点,用temp结点记录。
- 然后判断栈不为空或者temp不为空,进入循环。
- 先将temp的所有左子树全部压栈。
- 然后出栈栈顶结点,打印
- 然后再判断出栈的这个结点有没有右孩子,如果有,将右孩子入栈
- 再将右孩子结点 的所有左孩子全部入栈,即从第3步开始循环。
// 中序遍历(非递归)
void nonInthread(Node tree) {
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp = null;
temp = tree;
while (!stack.IsEmpty() || temp != null) {
while (temp != null) {
stack.push(temp);
temp = temp.lchild;
}
temp = stack.pop();
System.out.println(temp.name);
temp = temp.rchild;
}
}
中序的非递归遍历,和上述的先序的第二种非递归遍历比较,会发现,只改变了打印的位置,其他的都没有变化,所以建议记住,先序的第二种算法。
4.2 树的非递归后序遍历
后序我会给出,自己总结的三种方法,各有特点,其中第一种可以和前两对应起来,改动较小,对应编程能力差的同学,可以着重学习,后两种方法也很nice,想要多学点的同学,也可以学习。
(1)第一种:根据后序打印特点:左右跟。
我们根据先序的过程,先访问根节点,再访问左孩子,最后访问右孩子,根据具体的顺序,我们可以发现其实后序只需要,把先序打印的过程,换成一个如另一个栈的过程,所以我们需要两个栈,再把先将左孩子压栈,后将右孩子压栈,变成先将右孩子压栈,再将左孩子压栈,最后我们的第二个栈中就得到了后序的倒序,我们再将第二个栈出栈,打印就行。
// 后序遍历(非递归3)
void nonPostOrder3(Node tree) {
Seqstack stack2 = new Seqstack();
stack2.InitStack(20);
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp = null;
temp = tree;
while (!stack.IsEmpty() || temp != null) {
while (temp != null) {
stack2.push(temp); //这行改变
stack.push(temp);
temp = temp.rchild; //这行改变
}
temp = stack.pop();
temp = temp.lchild;
}
while (!stack2.IsEmpty()) { //把第二个栈全部出栈打印
temp = stack2.pop();
System.out.println(temp.name);
}
}
后两种方法,都需要明白一个特点:
我们可以观察前序、中序、后序的过程,根据他们是第几次访问根节点然后再打印的特点,我们可以发现:
- 先序都是先访问根节点,然后打印,然后再访问他的左孩子,把他的左孩子又看出根节点,然后打印,又去访问他的左孩子,如果左孩子不存在,去访问他的右孩子,把他的右孩子又看成根节点,一直重复上面的步骤。(这块的根节点指的是每一个结点都看成根节点,不是特指树的根节点)。
- 中序都是先访问根节点,然后访问所有的左孩子,左孩子全部访问结束,再回来访问根节点,打印根节点,再去访问右孩子,又去访问右孩子的所有左孩子,一直重复上面步骤。
- 后序都是先访问根节点,再访问他的全部左孩子,左孩子全部访问结束,再回来访问他的根节点,又访问他的全部右孩子,右孩子全部访问结束,再反过来打印根节点。(这块的根节点指的是每一个结点都看成根节点,不是特指树的根节点)。
总结一个特点:
- 先序:第一次访问一个结点时打印
- 中序:第二次访问一个结点时打印
- 后序:第三次访问一个结点时打印
后序什么时候打印,可以根据他是不是访问完右孩子过来的,如果是访问完右孩子过来的,就让他打印,否则去遍历他的右孩子。
下面的两种方法都是根据,这个特点完成的,这是有点稍微的区别。
(2)第二种:多创建一个结点,用来记录出栈的元素,进行判断
// 后序遍历(非递归1)
void nonPostOrder(Node tree) {
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp, r = null; //r用来记录
temp = tree;
while (temp != null || !stack.IsEmpty()) {
if (temp != null) {
stack.push(temp);
temp = temp.lchild;
} else {
temp = stack.getTop();
if (temp.rchild != null && temp.rchild != r) {
temp = temp.rchild;
} else {
temp = stack.pop();
System.out.println(temp.name);
r = temp;
temp = null;
}
}
}
}
(3)第三种:利用在结点中,多一个属性,用来记录是第几次访问
// 后序遍历(非递归2)
void nonPostOrder2(Node tree) {
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp;
temp = tree;
stack.push(temp);
temp = temp.lchild;
while (!stack.IsEmpty() || temp != null) {
while (temp != null) {
stack.push(temp);
temp = temp.lchild;
}
temp = stack.pop();
if (temp.rchild != null && temp.status == false) {
stack.push(temp);
temp.status = true;
temp = temp.rchild;
} else {
System.out.println(temp.name);
temp = null;
}
}
}
5. 树的层序遍历
用到了队列
// 层序遍历
void cengxu(Node tree) {
Node temp = null;
queue.front = queue.rear = 0;
if (tree == null) {
System.out.println("该树为空");
return;
}
queue.enterQueue(tree);
while (queue.front != queue.rear) {
temp = queue.deleteQueue();
System.out.println(temp.name);
if (temp.lchild != null) {
queue.enterQueue(temp.lchild);
}
if (temp.rchild != null) {
queue.enterQueue(temp.rchild);
}
}
}
6. 计算树的深度
// 递归计算树的深度
int high(Node t) {
int h = 0, left = 0, right = 0;
if (t == null) {
return 0;
}
left = high(t.lchild);
right = high(t.rchild);
h = (left > right ? left : right) + 1;
return h;
}
7. 反转二叉树
7.1 递归方法
// 反转二叉树(递归)
void recReversal(Node tree) {
if (tree == null) {
return;
}
if (tree.lchild == null && tree.rchild == null) {
return;
}
Node temp = tree.lchild;
tree.lchild = tree.rchild;
tree.rchild = temp;
recReversal(tree.lchild);
recReversal(tree.rchild);
}
7.2 非递归算法
用到栈
// 反转二叉树(非递归)
void nonRecReversal(Node tree) {
Node t = null;
if (tree == null) {
System.out.println("树为空");
return;
}
stack.push(tree);
while (!stack.IsEmpty()) {
Node temp = stack.pop();
t = temp.lchild;
temp.lchild = temp.rchild;
temp.rchild = t;
if (temp.rchild != null) {
stack.push(temp.rchild);
}
if (temp.lchild != null) {
stack.push(temp.lchild);
}
}
}
8. 统计结点数(递归)
// 统计结点数(递归)
int getNodeNum(Node tree) {
int nodes = 0;
if (tree == null) {
return 0;
}
else {
nodes = 1 + getNodeNum(tree.lchild) + getNodeNum(tree.rchild);
}
return nodes;
}
9. 统计叶子结点数(递归)
//统计叶子结点数(递归)
int getLeafNum(Node tree) {
if(tree == null) {
return 0;
}
if(tree.lchild == null &&tree.rchild == null) {
return 1;
}
return getLeafNum(tree.lchild) + getLeafNum(tree.rchild);
}
10. 判断一个结点是否是一个子树上的结点(递归)
//找一个结点是否是一个子树的结点
boolean Ishave(Node tree,Node node) {
if(node == null || tree == null) {
return false;
}
if(tree.name.equals(node.name)) {
return true;
}
else {
return Ishave(tree.lchild, node) | Ishave(tree.rchild, node);
}
}
11. 代码的综合
对上面讲述所有代码算法的综合
树的创建和操作:
//class Tree
import java.util.Scanner;
public class Tree {
// 声明输入流
Scanner scanner;
// 声明一个队列(层序遍历会用到)
Seqqueue queue;
// 声明一个栈(非递归的先序,中序,后序会用到)
Seqstack stack;
public Tree() {
// TODO Auto-generated constructor stub
// 初始化输入流
this.scanner = new Scanner(System.in);
// 初始化队列
this.queue = new Seqqueue();
this.queue.initQueue(10);
// 初始化栈
this.stack = new Seqstack();
this.stack.InitStack(20);
}
// 递归创建树(树的头节点)
Node createBiTree(Node tree) {
String string = scanner.nextLine();
if (string.equals(".")) {
return null;
}
tree = new Node();
tree.name = string;
tree.lchild = createBiTree(tree.lchild);
tree.rchild = createBiTree(tree.rchild);
return tree;
}
// 先序遍历(递归)
void PreOrder(Node tree) {
if (tree != null) {
System.out.println(tree.name);
PreOrder(tree.lchild);
PreOrder(tree.rchild);
}
}
// 先序遍历(非递归)
void nonPreOrder(Node tree) {
Node temp = null;
if (tree == null) {
System.out.println("树不存在");
return;
}
stack.push(tree);
while (!stack.IsEmpty()) {
temp = stack.pop();
System.out.println(temp.name);
if (temp.rchild != null) {
stack.push(temp.rchild);
}
if (temp.lchild != null) {
stack.push(temp.lchild);
}
}
}
// 先序遍历(非递归)
void nonPreOrder2(Node tree) {
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp = null;
temp = tree;
while (!stack.IsEmpty() || temp != null) {
while (temp != null) {
System.out.println(temp.name);
stack.push(temp);
temp = temp.lchild;
}
temp = stack.pop();
temp = temp.rchild;
}
}
// 中序遍历(递归)
void Inthread(Node tree) {
if (tree != null) {
Inthread(tree.lchild);
System.out.println(tree.name);
Inthread(tree.rchild);
}
}
// 中序遍历(非递归)
void nonInthread(Node tree) {
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp = null;
temp = tree;
while (!stack.IsEmpty() || temp != null) {
while (temp != null) {
stack.push(temp);
temp = temp.lchild;
}
temp = stack.pop();
System.out.println(temp.name);
temp = temp.rchild;
}
}
// 后序遍历(递归)
void PostOrder(Node tree) {
if (tree != null) {
PostOrder(tree.lchild);
PostOrder(tree.rchild);
System.out.println(tree.name);
}
}
// 后序遍历(非递归1)
void nonPostOrder(Node tree) {
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp, r = null;
temp = tree;
while (temp != null || !stack.IsEmpty()) {
if (temp != null) {
stack.push(temp);
temp = temp.lchild;
} else {
temp = stack.getTop();
if (temp.rchild != null && temp.rchild != r) {
temp = temp.rchild;
} else {
temp = stack.pop();
System.out.println(temp.name);
r = temp;
temp = null;
}
}
}
}
// 后序遍历(非递归2)
void nonPostOrder2(Node tree) {
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp;
temp = tree;
stack.push(temp);
temp = temp.lchild;
while (!stack.IsEmpty() || temp != null) {
while (temp != null) {
stack.push(temp);
temp = temp.lchild;
}
temp = stack.pop();
if (temp.rchild != null && temp.status == false) {
stack.push(temp);
temp.status = true;
temp = temp.rchild;
} else {
System.out.println(temp.name);
temp = null;
}
}
}
// 后序遍历(非递归3)
void nonPostOrder3(Node tree) {
Seqstack stack2 = new Seqstack();
stack2.InitStack(20);
if (tree == null) {
System.out.println("树不存在");
return;
}
Node temp = null;
temp = tree;
while (!stack.IsEmpty() || temp != null) {
while (temp != null) {
stack2.push(temp);
stack.push(temp);
temp = temp.rchild;
}
temp = stack.pop();
temp = temp.lchild;
}
while (!stack2.IsEmpty()) {
temp = stack2.pop();
System.out.println(temp.name);
}
}
// 层序遍历
void cengxu(Node tree) {
Node temp = null;
queue.front = queue.rear = 0;
if (tree == null) {
System.out.println("该树为空");
return;
}
queue.enterQueue(tree);
while (queue.front != queue.rear) {
temp = queue.deleteQueue();
System.out.println(temp.name);
if (temp.lchild != null) {
queue.enterQueue(temp.lchild);
}
if (temp.rchild != null) {
queue.enterQueue(temp.rchild);
}
}
}
// 递归计算树的深度
int high(Node t) {
int h = 0, left = 0, right = 0;
if (t == null) {
return 0;
}
left = high(t.lchild);
right = high(t.rchild);
h = (left > right ? left : right) + 1;
return h;
}
// 反转二叉树(递归)
void recReversal(Node tree) {
if (tree == null) {
return;
}
if (tree.lchild == null && tree.rchild == null) {
return;
}
Node temp = tree.lchild;
tree.lchild = tree.rchild;
tree.rchild = temp;
recReversal(tree.lchild);
recReversal(tree.rchild);
}
// 反转二叉树(非递归)
void nonRecReversal(Node tree) {
Node t = null;
if (tree == null) {
System.out.println("树为空");
return;
}
stack.push(tree);
while (!stack.IsEmpty()) {
Node temp = stack.pop();
t = temp.lchild;
temp.lchild = temp.rchild;
temp.rchild = t;
if (temp.rchild != null) {
stack.push(temp.rchild);
}
if (temp.lchild != null) {
stack.push(temp.lchild);
}
}
}
//统计叶子结点数(递归)
int getLeafNum(Node tree) {
if(tree == null) {
return 0;
}
if(tree.lchild == null &&tree.rchild == null) {
return 1;
}
return getLeafNum(tree.lchild) + getLeafNum(tree.rchild);
}
// 统计结点数(递归)
int getNodeNum(Node tree) {
int nodes = 0;
if (tree == null) {
return 0;
}
else {
nodes = 1 + getNodeNum(tree.lchild) + getNodeNum(tree.rchild);
}
return nodes;
}
//判断一个结点是否是一个子树的结点
boolean Ishave(Node tree,Node node) {
if(node == null || tree == null) {
return false;
}
if(tree.name.equals(node.name)) {
return true;
}
else {
return Ishave(tree.lchild, node) | Ishave(tree.rchild, node);
}
}
}
栈的结构:
//class Seqstack
public class Seqstack {
// 节点数组(模拟顺序栈)
public Node[] nodes;
// 栈顶下标
public int top;
public Seqstack() {
// TODO Auto-generated constructor stub
this.top = -1;
}
// 初始化栈
public void InitStack(int size) {
this.nodes = new Node[size];
this.top = -1;
}
// 判断栈空
public boolean IsEmpty() {
if (this.top == -1) {
return true;
}
return false;
}
// 判断栈满
public boolean IsFull() {
if (this.top == this.nodes.length - 1) {
return true;
}
return false;
}
// 入栈
public void push(Node node) {
if (IsFull()) {
System.out.println("栈已经满了,入栈失败");
return;
}
this.top++;
this.nodes[this.top] = node;
}
// 出栈
public Node pop() {
if (IsEmpty()) {
System.out.println("栈已经空了,出栈失败");
return null;
}
Node node = this.nodes[this.top];
this.top--;
return node;
}
// 清空栈
public void ClearStack() {
// 如果不为空,就一直出栈
while (!IsEmpty()) {
Node node = pop();
}
}
// 拿到栈顶元素
public Node getTop() {
if (IsEmpty()) {
System.out.println("栈中没有元素");
return null;
}
return this.nodes[this.top];
}
}
循环队列的结构:
//class Seqqueue
public class Seqqueue {
public Node[] queue;
public int QUEUE_SIZE;
public int front;
public int rear;
//初始化队列
public void initQueue(int size) {
this.QUEUE_SIZE = size;
this.queue = new Node[size];
this.front = 0;
this.rear = 0;
}
//判断队列是否为空
public boolean isEmpty() {
if(this.front == this.rear ) {
return true;
}
else {
return false;
}
}
//判断队列是否为满
public boolean isFull() {
if((this.front % this.QUEUE_SIZE) == ((this.rear+1) % this.QUEUE_SIZE)) {
return true;
}
else {
return false;
}
}
//入队
public void enterQueue(Node node) {
if(isFull()) {
System.out.println("队列已经满了,入队失败");
return;
}
this.queue[this.rear] = node;
this.rear ++;
this.rear = this.rear % this.QUEUE_SIZE;
}
//出队
public Node deleteQueue() {
if(isEmpty()){
System.out.println("队列为空,出队失败");
return null;
}
this.front = this.front % this.QUEUE_SIZE;
Node node = this.queue[this.front];
this.front++;
return node;
}
}
给个测试用例:
//class Client
public class Client {
public static void main(String[] args) {
Tree tree = new Tree();
Node node = null ;
Node node4 = new Node("a");
node = tree.createBiTree(node);
System.out.println("创建完成");
System.out.println("递归先序遍历");
tree.PreOrder(node);
System.out.println("---------------------");
System.out.println("非递归先序遍历");
tree.nonPreOrder(node);
System.out.println("---------------------");
System.out.println("非递归先序遍历2");
tree.nonPreOrder2(node);
System.out.println("---------------------");
System.out.println("递归中序遍历");
tree.Inthread(node);
System.out.println("---------------------");
System.out.println("非递归中序遍历");
tree.nonInthread(node);
System.out.println("---------------------");
System.out.println("后序遍历(递归)");
tree.PostOrder(node);
System.out.println("---------------------");
System.out.println("后序遍历(非递归)");
tree.nonPostOrder(node);
System.out.println("---------------------");
System.out.println("后序遍历(非递归2)");
tree.nonPostOrder2(node);
System.out.println("---------------------");
System.out.println("后序遍历(非递归3)");
tree.nonPostOrder3(node);
System.out.println("---------------------");
System.out.println("层序遍历");
tree.cengxu(node);
System.out.println("---------------------");
System.out.println("树的深度"+tree.high(node));
System.out.println("---------------------");
System.out.println("递归反转二叉树");
tree.recReversal(node);
tree.cengxu(node);
System.out.println("-------------------");
System.out.println("非递归反转二叉树");
tree.nonRecReversal(node);
tree.cengxu(node);
System.out.println("-------------------");
System.out.println("树的叶子数:" + tree.getLeafNum(node));
System.out.println("--------------------");
System.out.println("树的结点数:" + tree.getNodeNum(node));
System.out.println("--------------------");
System.out.println("判断一个结点是否是一个子树上面的结点");
System.out.println(tree.Ishave(node.lchild, node4));
}
}
网友评论