二叉树的三种遍历:递归和非递归版本
import java.util.*;
public class treebianli {
public static class Node{
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
//树遍历的递归版本
public static void preOrderRecur(Node head) {
if(head == null) return ;
System.out.println(head.value + " ");//放前面就是先序遍历
preOrderRecur(head.left);
preOrderRecur(head.right);
}
public static void inOrderRecur(Node head) {
if(head == null) return ;
inOrderRecur(head.left);
System.out.println(head.value + " ");//放中间就是中序遍历
inOrderRecur(head.right);
}
public static void posOrderRecur(Node head) {
if(head == null) return ;
posOrderRecur(head.left); //先左后右
posOrderRecur(head.right);
System.out.println(head.value + " "); //放最后就是后序遍历
}
//树遍历的非递归版本
//先序:使用栈,见笔记本26页
/**
* 先序的特点是遇到头就打印,然后打印左子树,然后打印右子树,任何一颗子树也是这种情况,
* 所以使用栈,先进右子树头结点,然后进左子树头节点。和树的层次化打印一个道理,只不过是用的
* 队列。不论是哪一种,直接看三个节点的树过程,并以此写代码,最后加上递归终止条件即可。
* */
public static void preOrder(Node head) {
System.out.println("pre-order");
if(head != null) {
Stack<Node> stack = new Stack<Node>();
stack.add(head);
while(!stack.isEmpty()) {
head = stack.pop();
System.out.println(head.value+" ");
if(head.right!=null)
stack.add(head.right);//先进后出
if(head.left!=null)
stack.add(head.left);
}
}
System.out.println();
}
/**
* 中序遍历:
* 规则是:1.节点进来,将树的左边界全部压进栈,当遍历到最左节点的左空节点时,弹出
* 栈顶元素,并打印,然后让当前节点等于打印节点的右子节点,继续遍历。
* 为什么这么做?因为中序:左全部进完,弹出栈顶并打印后,要看让它等于右孩子,这时任然为空,
* 继续弹出栈顶,即父节点,打印,当前节点等于右孩子节点,这样就进入右子树。
* */
public static void inOrder(Node head) {
System.out.println("inOrder");
if(head!=null) {
Stack<Node> stack = new Stack<>();
// stack.add(head);注意这里是注释,因为下面一进去就是加头结点,所以条件有节点不为空,方便进循环。
while(!stack.isEmpty() || head != null) {
if(head != null) {
head = stack.push(head);
head = head.left; //把左边界的左节点全部压到栈中,画个图就好理解了。
}
else { //为空,说明跑到左边最后一个节点的左空节点上了
head = stack.pop();
System.out.println(head.value +" ");
head = head.right; //此时还为空,还会来到这儿,打印子树头结点,这样这里就进入了右子树,
//并且右子树也有左子树和右子树,情况一样,所以同一级的左子树和头结点打印完直接进同一级的右子树,
//当右子树也打印完,又回到这里,回到该层的上一级,这样循环往复,直至中序打印完整棵树。
}
}
}
}
//后续遍历:思路是先序遍历的加工,因为先序遍历是按照中左右打印,若果改成中右左打印,但是不打印,放到另一个栈中,
//后面再从中打印,这样就变成了 树的后序遍历。
public static void posOrder(Node head) {
System.out.println("posOrder");
if(head!=null) {
Stack<Node> s1 = new Stack<>();
Stack<Node> s2 = new Stack<>();
s1.push(head);
while(!s1.isEmpty()) {
head = s1.pop();
s2.push(head);
if(head.left!=null)
s1.push(head.left);
if(head.right!=null)
s1.push(head.right);
}
while(!s2.isEmpty())
System.out.print(s2.pop().value + " ");
}
//不论是先序中序还是后序,在复习的时候带上整个流程去复习就没问题。
System.out.println();
}
//从上到下打印二叉树
//思路和先序遍历一样,只不过是用队列存储。就是因为用队列,
//所以导致节点的左树节点打印后就直接打印右树节点,从而实现由上到下按层遍历。
//但是树的先序遍历是栈,先进后出,所以左节点打印后马上压入左节点的子节点,
//这样左节点的子节点又比右子树先打印,从而实现先序遍历:
//头结点进去后,打印,然后对打印节点的左节点进去,右节点进去,然后又弹出,
//重复执行。就实现了打印一棵树。
public static void printFromTopToBottom(Node head) {
ArrayList<Integer> arr = new ArrayList<>();
if (head == null) return ;
Queue<Node> qu = new LinkedList<>();//一定注意队列的实现方式,
//且队列先进先出,只有优先队列(Priority)才会排好序,弹出最小值,注意区别.
qu.add(head);
while(!qu.isEmpty()) {
head = qu.poll(); //队列是poll弹出,栈是pop()
arr.add(head.value);
if(head.left!=null) //注意和先序的区别
qu.add(head.left);
if(head.right != null)
qu.add(head.right); //队列和arr都是add,栈是push
}
System.out.println("从上到下打印一颗二叉树");
System.out.println(arr);
}
public static void main(String[] args) {
Node head = new Node(5);
head.left = new Node(3);
head.right = new Node(8);
head.left.left = new Node(2);
head.left.right = new Node(4);
head.left.left.left = new Node(1);
//树的三种非递归遍历
preOrder(head);
inOrder(head);
posOrder(head); //这里是在main函数中的变量,所以是局部变量,不影响其他的遍历
//层次化打印一棵树,从上到下打印
printFromTopToBottom(head);
}
}
二叉树相关
最新推荐文章于 2024-02-16 00:36:56 发布