先序遍历:
1.访问根节点
2.先序遍历其左子树
3.先序遍历其右子树
记忆:根左右
例子:
代码实现:
1.首先创建一棵树
//生成树
public static TreeNode initTree(){
TreeNode G = new TreeNode("G", null, null);
TreeNode H = new TreeNode("H", null, null);
TreeNode D = new TreeNode("D", G, H);
TreeNode B = new TreeNode("B", D, null);
TreeNode I = new TreeNode("I", null, null);
TreeNode E = new TreeNode("E", null, I);
TreeNode F = new TreeNode("F", null, null);
TreeNode C = new TreeNode("C", E, F);
TreeNode A = new TreeNode("A", B, C);
return A;
}
树的结构如图所示:
2.先序遍历的实现
//先序遍历:根左右
// 递归实现
//但是在数据量大的情况下会出现辅助空间占用过多,占用大量的内存
public static void preOrderRecursion(TreeNode T) {
if (T == null) {
return;
}
System.out.print(T.value + " ");
preOrderRecursion(T.lchild);
preOrderRecursion(T.rchild);
}
/*
先序遍历的非递归算法
因为把右边的先存起来,后来倒序读出来,先入后出,和栈一样
采用栈来实现
前序遍历有个特点就是遍历到左叶子节点的时候需要回头再去遍历之前未遍历到的右节点
所以需要先把右边的压入栈中,左边的一直读到左叶子节点再去遍历栈中右边的节点
*/
public static void preOrderNormal(TreeNode T) {
//变量用于存储弹出的node
TreeNode p;
Stack<TreeNode> stack=new Stack<>();
stack.push(T) ; //先把根节点压入栈中
//当树和栈都不为空时
while (T!=null&&!stack.isEmpty()){
p=stack.pop();//弹出栈顶
System.out.print(p.value+" ");
//先把右边的压入,因为左边的先读出来
if (p.rchild!=null){
stack.push(p.rchild);
}
if (p.lchild!=null){
stack.push(p.lchild);
}
}
}
结果如图:符合先序遍历的规则
总结:
先序遍历的特点就是“根左右”,一直往左读,右边入栈等左边读完再读,比较容易实现
中序遍历:
1.遍历器左子树
2.访问根节点
3.中序遍历其右子树
记忆:左根右
例子:
代码实现:
1.中序遍历的实现
/*
中序遍历:左根右
递归实现
一直遍历左边,到叶子节点return到父节点,然后右边,递归
*/
public static void inOrderRecursion(TreeNode T){
if (T==null){
return;
}
inOrderRecursion(T.lchild);
System.out.print(T.value + " ");
inOrderRecursion(T.rchild);
}
/*
中序遍历
循环实现
遇到一个节点,压栈,遍历左子树
左子树遍历完,弹出比那个访问
然后按照右指针再去中序遍历该节点的右子树
*/
public static void inOrderNormal(TreeNode T){
Stack<TreeNode> stack=new Stack<>();
//用于赋值的局部变量
TreeNode p=T;
while (p!=null||!stack.isEmpty()){
while (p!=null){//当左右子项不为空时
stack.push(p);//有子树的直接压栈
p=p.lchild;
}
if (!stack.isEmpty()){
p=stack.pop();//没有左子项就出栈
System.out.print(p.value+" ");
p=p.rchild;//访问右子树,若为空则直接弹出父节点
}
}
}
结果:
总结:
右序遍历为“左根右”,所以需要先把根节点压入栈中,遍历其左子树,到叶子节点后,弹出并访问父节点,再看右子树中的元素