一。什么是二叉树遍历:
所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问题。遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。
二。三种遍历顺序:
① NLR:前序遍历(Preorder Traversal 亦称(先序遍历))访问根结点的操作发生在遍历其左右子树之前。
② LNR:中序遍历(Inorder Traversal)
访问根结点的操作发生在遍历其左右子树之中(间)。
③ LRN:后序遍历(Postorder Traversal)
访问根结点的操作发生在遍历其左右子树之后。
三。三种遍历的迭代实现
public class BinTree {
public final static int MAX = 40;
BinTree[] elements = new BinTree[MAX];// 层次遍历时保存各个节点
int front;// 层次遍历时队首
int rear;// 层次遍历时队尾
private Object data; // 数据元数
private BinTree left, right; // 指向左,右孩子结点的链
public BinTree() {
}
public BinTree(Object data) { // 构造有值结点
this.data = data;
left = right = null;
}
public BinTree(Object data, BinTree left, BinTree right) { // 构造有值结点
this.data = data;
this.left = left;
this.right = right;
}
public String toString() {
return data.toString();
}
// 前序遍历二叉树
public static void preOrder(BinTree parent) {
if (parent == null)
return;
System.out.print(parent.data + " ");
preOrder(parent.left);
preOrder(parent.right);
}
// 中序遍历二叉树
public void inOrder(BinTree parent) {
if (parent == null)
return;
inOrder(parent.left);
System.out.print(parent.data + " ");
inOrder(parent.right);
}
// 后序遍历二叉树
public void postOrder(BinTree parent) {
if (parent == null)
return;
postOrder(parent.left);
postOrder(parent.right);
System.out.print(parent.data + " ");
}
// 层次遍历二叉树 (核心逻辑:front表示打印到哪个下标了-走的慢每次循环一个,rear表示收录到哪个下标了-走的块每次循环可能收录左右两)
public void LayerOrder(BinTree parent) {
elements[0] = parent;
front = 0;
rear = 1;
while (front < rear) {
try {
if (elements[front].data != null) {
System.out.print(elements[front].data + " ");
if (elements[front].left != null)
elements[rear++] = elements[front].left;
if (elements[front].right != null)
elements[rear++] = elements[front].right;
front++;
}
} catch (Exception e) {
break;
}
}
}
// 返回树的叶节点个数
public int leaves() {
if (this == null)
return 0;
if (left == null && right == null)
return 1;
return (left == null ? 0 : left.leaves())
+ (right == null ? 0 : right.leaves());
}
// 结果返回树的高度(核心逻辑:左右树取较大者,每次升一级)
public int height() {
int heightOfTree;
if (this == null)
return -1;
int leftHeight = (left == null ? 0 : left.height());
int rightHeight = (right == null ? 0 : right.height());
heightOfTree = leftHeight < rightHeight ? rightHeight : leftHeight;
return 1 + heightOfTree;
}
// 如果对象不在树中(核心逻辑:先根遍历,找不到的节点都返回-1,知道找到返回1,然后后续均加一级返回)
public int level(Object object) {
int levelInTree;
if (this == null)
return -1;
if (object == data)
return 1;// 规定根节点为第一层
int leftLevel = (left == null ? -1 : left.level(object));
int rightLevel = (right == null ? -1 : right.level(object));
if (leftLevel < 0 && rightLevel < 0)
return -1;
levelInTree = leftLevel < rightLevel ? rightLevel : leftLevel;
return 1 + levelInTree;
}
// 将树中的每个节点的孩子对换位置(核心逻辑:从下往上,从左往右依次交换左右节点)
public void reflect() {
if (this == null)
return;
if (left != null)
left.reflect();
if (right != null)
right.reflect();
BinTree temp = left;
left = right;
right = temp;
}
// 将树中的所有节点移走,并输出移走的节点(核心逻辑:中根遍历,先删除没有左右节点的,然后中间,然后右边,从下往上依次删除)
public void defoliate() {
if (this == null)
return;
// 若本节点是叶节点,则将其移走
if (left == null && right == null) {
System.out.print(this + " ");
data = null;
return;
}
// 移走左子树若其存在
if (left != null) {
left.defoliate();
left = null;
}
// 移走本节点,放在中间表示中跟移走...
String innerNode = this + " ";
System.out.print(data + " ");
data = null;
// 移走右子树若其存在
if (right != null) {
right.defoliate();
right = null;
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
BinTree e = new BinTree("E");
BinTree g = new BinTree("G");
BinTree h = new BinTree("H");
BinTree i = new BinTree("I");
BinTree d = new BinTree("D", null, g);
BinTree f = new BinTree("F", h, i);
BinTree b = new BinTree("B", d, e);
BinTree c = new BinTree("C", f, null);
BinTree tree = new BinTree("A", b, c);
System.out.println("前序遍历二叉树结果: ");
tree.preOrder(tree);
System.out.println();
System.out.println("中序遍历二叉树结果: ");
tree.inOrder(tree);
System.out.println();
System.out.println("后序遍历二叉树结果: ");
tree.postOrder(tree);
System.out.println();
System.out.println("层次遍历二叉树结果: ");
tree.LayerOrder(tree);
System.out.println();
System.out.println("F所在的层次: " + tree.level("F"));
System.out.println("这棵二叉树的高度: " + tree.height());
System.out.println("--------------------------------------");
tree.reflect();
System.out.println("交换每个节点的孩子节点后......");
System.out.println("前序遍历二叉树结果: ");
tree.preOrder(tree);
System.out.println();
System.out.println("中序遍历二叉树结果: ");
tree.inOrder(tree);
System.out.println();
System.out.println("后序遍历二叉树结果: ");
tree.postOrder(tree);
System.out.println();
System.out.println("层次遍历二叉树结果: ");
tree.LayerOrder(tree);
System.out.println();
System.out.println("F所在的层次: " + tree.level("F"));
System.out.println("这棵二叉树的高度: " + tree.height());
System.out.println("--------------------------------------");
tree.defoliate();
}
}
运行结果:
前序遍历二叉树结果:
A B D G E C F H I
中序遍历二叉树结果:
D G B E A H F I C
后序遍历二叉树结果:
G D E B H I F C A
层次遍历二叉树结果:
A B C D E F G H I
F所在的层次: 3
这棵二叉树的高度: 4
--------------------------------------
交换每个节点的孩子节点后......
前序遍历二叉树结果:
A C F I H B E D G
中序遍历二叉树结果:
C I F H A E B G D
后序遍历二叉树结果:
I H F C E G D B A
层次遍历二叉树结果:
A C B F E D I H G
F所在的层次: 3
这棵二叉树的高度: 4
--------------------------------------
C I F H A E B G D
四。二叉树的非递归的遍历实现( B<--A-->C D<--B-->E C-->F)
1.先根遍历
(先访问根节点,再访问左子树,后访问右子树。而对于每个子树来说,又按照同样的访问顺序进行遍历)
实现思路如下:
对于任一节点P,
1)输出节点P,然后将其入栈,再看P的左孩子是否为空;
2)若P的左孩子不为空,则置P的左孩子为当前节点,重复1)的操作;
3)若P的左孩子为空,则将栈顶节点出栈,但不输出,并将出栈节点的右孩子置为当前节点,看其是否为空;
4)若不为空,则循环至1)操作;
5)如果为空,则继续出栈,但不输出,同时将出栈节点的右孩子置为当前节点,看其是否为空,重复4)和5)操作;
6)直到当前节点P为NULL并且栈空,遍历结束。
详细分析其先序遍历的非递归实现过程:
实现思路如下:
对于任一节点P,
1)输出节点P,然后将其入栈,再看P的左孩子是否为空;
2)若P的左孩子不为空,则置P的左孩子为当前节点,重复1)的操作;
3)若P的左孩子为空,则将栈顶节点出栈,但不输出,并将出栈节点的右孩子置为当前节点,看其是否为空;
4)若不为空,则循环至1)操作;
5)如果为空,则继续出栈,但不输出,同时将出栈节点的右孩子置为当前节点,看其是否为空,重复4)和5)操作;
6)直到当前节点P为NULL并且栈空,遍历结束。
详细分析其先序遍历的非递归实现过程:
- 首先,从根节点A开始,根据操作1),输出A,并将其入栈,由于A的左孩子不为空,根据操作2),将B置为当前节点,再根据操作1),将B输出,并将其入栈, 由于B的左孩子也不为空,根据操作2),将D置为当前节点,再根据操作1),输出D,并将其入栈,此时输出序列为ABD;
- 由于D的左孩子为空,根据操作3),将栈顶节点D出栈,但不输出,并将其右孩子置为当前节点;
- 由于D的右孩子为空,根据操作5),继续将栈顶节点B出栈,但不输出,并将其右孩子置为当前节点;
- 由于B的右孩子E不为空,根据操作1),输出E,并将其入栈,此时输出序列为:ABDE;
- 由于E的左孩子为空,根据操作3),将栈顶节点E出栈,但不输出,并将其右孩子置为当前节点;
- 由于E的右孩子为空,根据操作5),继续将栈顶节点A出栈,但不输出,并将其右孩子置为当前节点;
- 由于A的右孩子C不为空,根据操作1),输出C,并将其入栈,此时输出序列为:ABDEC;
- 由于A的左孩子F不为空,根据操作2),则将F置为当前节点,再根据操作1),输出F,并将其入栈,此时输出序列为:ABDECF;
- 由于F的左孩子为空,根据操作3),将栈顶节点F出栈,但不输出,并将其右孩子置为当前节点;
- 由于F的右孩子为空,根据操作5),继续将栈顶元素C出栈,但不输出,并将其右孩子置为当前节点;
- 此时栈空,且C的右孩子为NULL,因此遍历结束。
void pre_traverse(BTree pTree)
{
PSTACK stack = create_stack(); //创建一个空栈
BTree node_pop; //用来保存出栈节点
BTree pCur = pTree; //定义用来指向当前访问的节点的指针
while(pCur || !is_empty(stack))
{
//循环遍历根和左孩子
printf("%c ", pCur->data);
push_stack(stack,pCur);
pCur = pCur->pLchild;
//到最底部时开始遍历右孩子
while(!pCur && !is_empty(stack))
{
pCur = getTop(stack);
pop_stack(stack,&node_pop);
pCur = pCur->pRchild;
}
}
}
2.中根遍历
(先访问左子树,再访问根节点,后访问右子树,而对于每个子树来说,又按照同样的访问顺序进行遍历)
实现思路如下:
对于任一节点P,
1)若P的左孩子不为空,则将P入栈并将P的左孩子置为当前节点,然后再对当前节点进行相同的处理;
2)若P的左孩子为空,则输出P节点,而后将P的右孩子置为当前节点,看其是否为空;
3)若不为空,则重复1)和2)的操作;
4)若为空,则执行出栈操作,输出栈顶节点,并将出栈的节点的右孩子置为当前节点,看起是否为空,重复3)和4)的操作;
5)直到当前节点P为NULL并且栈为空,则遍历结束。
详细分析其中序遍历的非递归实现过程:
实现思路如下:
对于任一节点P,
1)若P的左孩子不为空,则将P入栈并将P的左孩子置为当前节点,然后再对当前节点进行相同的处理;
2)若P的左孩子为空,则输出P节点,而后将P的右孩子置为当前节点,看其是否为空;
3)若不为空,则重复1)和2)的操作;
4)若为空,则执行出栈操作,输出栈顶节点,并将出栈的节点的右孩子置为当前节点,看起是否为空,重复3)和4)的操作;
5)直到当前节点P为NULL并且栈为空,则遍历结束。
详细分析其中序遍历的非递归实现过程:
- 首先,从根节点A开始,A的左孩子不为空,根据操作1)将A入栈,接着将B置为当前节点,B的左孩子也不为空,根据操作1),将B也入栈,接着将D置为当前节 点,由于D的左子树为空,根据操作2),输出D;
- 由于D的右孩子也为空,根据操作4),执行出栈操作,将栈顶结点B出栈,并将B置为当前节点,此时输出序列为DB;
- 由于B的右孩子不为空,根据操作3),将其右孩子E置为当前节点,由于E的左孩子为空,根据操作1),输出E,此时输出序列为DBE;
- 由于E的右孩子为空,根据操作4),执行出栈操作,将栈顶节点A出栈,并将节点A置为当前节点,此时输出序列为DBEA;
- 此时栈为空,但当前节点A的右孩子并不为NULL,继续执行,由于A的右孩子不为空,根据操作3),将其右孩子C置为当前节点,由于C的左孩子不为空,根据操 作1),将C入栈,将其左孩子F置为当前节点,由于F的左孩子为空,根据操作2),输出F,此时输出序列为:DBEAF;
- 由于F的右孩子也为空,根据操作4),执行出栈操作,将栈顶元素C出栈,并将其置为当前节点,此时的输出序列为:DBEAFC;
- 由于C的右孩子为NULL,且此时栈空,根据操作5),遍历结束。
void in_traverse(BTree pTree)
{
PSTACK stack = create_stack(); //创建一个空栈
BTree node_pop; //用来保存出栈节点
BTree pCur = pTree; //定义指向当前访问的节点的指针
//直到当前节点pCur为NULL且栈空时,循环结束
while(pCur || !is_empty(stack))
{
if(pCur->pLchild)
{
//如果pCur的左孩子不为空,则将其入栈,并置其左孩子为当前节点
push_stack(stack,pCur);
pCur = pCur->pLchild;
}
else
{
//如果pCur的左孩子为空,则输出pCur节点,并将其右孩子设为当前节点,看其是否为空
printf("%c ", pCur->data);
pCur = pCur->pRchild;
//如果为空,且栈不空,则将栈顶节点出栈,并输出该节点,
//同时将它的右孩子设为当前节点,继续判断,直到当前节点不为空
while(!pCur && !is_empty(stack))
{
pCur = getTop(stack);
printf("%c ",pCur->data);
pop_stack(stack,&node_pop);
pCur = pCur->pRchild;
}
}
}
}
3.后根遍历
(先访问左子树,再访问右子树,后访问根节点,而对于每个子树来说,又按照同样的访问顺序进行遍历)
实现思路如下:
对于任一节点P,
1)先将节点P入栈;
2)若P不存在左孩子和右孩子,或者P存在左孩子或右孩子,但左右孩子已经被输出,则可以直接输出节点P,并将其出栈,将出栈节点P标记为上一个输出的节点,再将此时的栈顶结点设为当前节点;
3)若不满足2)中的条件,则将P的右孩子和左孩子依次入栈,当前节点重新置为栈顶结点,之后重复操作2);
4)直到栈空,遍历结束。
详细分析其后序遍历的非递归实现过程:
实现思路如下:
对于任一节点P,
1)先将节点P入栈;
2)若P不存在左孩子和右孩子,或者P存在左孩子或右孩子,但左右孩子已经被输出,则可以直接输出节点P,并将其出栈,将出栈节点P标记为上一个输出的节点,再将此时的栈顶结点设为当前节点;
3)若不满足2)中的条件,则将P的右孩子和左孩子依次入栈,当前节点重新置为栈顶结点,之后重复操作2);
4)直到栈空,遍历结束。
详细分析其后序遍历的非递归实现过程:
- 首先,设置两个指针:Cur指针指向当前访问的节点,它一直指向栈顶节点,每次出栈一个节点后,将其重新置为栈顶结点,Pre节点指向上一个访问的节点;
- Cur首先指向根节点A,Pre先设为NULL,由于A存在左孩子和右孩子,根据操作3),先将右孩子C入栈,再将左孩子B入栈,Cur改为指向栈顶结点B;
- 由于B的也有左孩子和右孩子,根据操作3),将E、D依次入栈,Cur改为指向栈顶结点D;
- 由于D没有左孩子,也没有右孩子,根据操作2),直接输出D,并将其出栈,将Pre指向D,Cur指向栈顶结点E,此时输出序列为:D;
- 由于E也没有左右孩子,根据操作2),输出E,并将其出栈,将Pre指向E,Cur指向栈顶结点B,此时输出序列为:DE;
- 由于B的左右孩子已经被输出,即满足条件Pre==Cur->lchild或Pre==Cur->rchild,根据操作2),输出B,并将其出栈,将Pre指向B,Cur指向栈顶结点C,此时输 出序列为:DEB;
- 由于C有左孩子,根据操作3),将其入栈,Cur指向栈顶节点F;
- 由于F没有左右孩子,根据操作2),输出F,并将其出栈,将Pre指向F,Cur指向栈顶结点C,此时输出序列为:DEBF;
- 由于C的左孩子已经被输出,即满足Pre==Cur->lchild,根据操作2),输出C,并将其出栈,将Pre指向C,Cur指向栈顶结点A,此时输出序列为:DEBFC;
- 由于A的左右孩子已经被输出,根据操作2),输出A,并将其出栈,此时输出序列为:DEBFCA;
- 此时栈空,遍历结束。
void beh_traverse(BTree pTree)
{
PSTACK stack = create_stack(); //创建一个空栈
BTree node_pop; //用来保存出栈的节点
BTree pCur; //定义指针,指向当前节点
BTree pPre = NULL; //定义指针,指向上一各访问的节点
//先将树的根节点入栈
push_stack(stack,pTree);
//直到栈空时,结束循环
while(!is_empty(stack))
{
//当前节点置为栈顶节点
pCur = getTop(stack);
//如果当前节点没有左右孩子,或者有左孩子或有孩子,但已经被访问输出,
//则直接输出该节点,将其出栈,将其设为上一个访问的节点
if((pCur->pLchild==NULL && pCur->pRchild==NULL) ||
(pPre!=NULL && (pCur->pLchild==pPre || pCur->pRchild==pPre)))
{
printf("%c ", pCur->data);
pop_stack(stack,&node_pop);
pPre = pCur;
}
else
{
//如果不满足上面两种情况,则将其右孩子左孩子依次入栈
if(pCur->pRchild != NULL)
push_stack(stack,pCur->pRchild);
if(pCur->pLchild != NULL)
push_stack(stack,pCur->pLchild);
}
}
}