二叉树前序中序后序javascript_详解二叉树遍历(前序、中序、后序、层次遍历、深度优先、广度优先)...

本文详细介绍了二叉树的四种遍历方法:前序遍历(根左右)、中序遍历(左根右)、后序遍历(左右根)和层次遍历。提供了Java代码实现,包括递归和非递归两种方式,并给出了每种遍历方式的具体结果。
摘要由CSDN通过智能技术生成

二叉树有多种遍历方法,有层次遍历、深度优先遍历、广度优先遍历等。

涉及到的代码都用Java编写。

1. 首先给出二叉树节点类

树节点:

class TreeNode{

int val;

//左子树

TreeNode left;

//右子树

TreeNode right;

//构造方法

TreeNode(int x) {

val = x;

}

}

无论是哪种遍历方法,考查节点的顺序都是一样的(思考做试卷的时候,人工遍历考查顺序)。只不过有时候考查了节点,将其暂存,需要之后的过程中输出。

如图所示,三种遍历方法(人工)得到的结果分别是:

先序:1246783 5

中序:4768213 5

后序:7864253 1

层次遍历:12345678

三种遍历方法的考查顺序一致,得到的结果却不一样,原因在于:

先序:考察到一个节点后,即刻输出该节点的值,并继续遍历其左右子树。(根左右)

中序:考察到一个节点后,将其暂存,遍历完左子树后,再输出该节点的值,然后遍历右子树。(左根右)

后序:考察到一个节点后,将其暂存,遍历完左右子树后,再输出该节点的值。(左右根)

层次遍历:只需按层次遍历即可

2. 先序遍历

2.1 递归先序遍历

递归先序遍历很容易理解,先输出节点的值,再递归遍历左右子树。中序和后序的递归类似,改变根节点输出位置即可。

// 递归先序遍历

public static void recursionPreorderTraversal(TreeNode root){

if (root != null) {

System.out.print(root.val + " ");

recursionPreorderTraversal(root.left);

recursionPreorderTraversal(root.right);

}

}

2.2 非递归先序遍历

因为要在遍历完节点的左子树后接着遍历节点的右子树,为了能找到该节点,需要使用栈来进行暂存。中序和后序也都涉及到回溯,所以都需要用到栈。

遍历过程参考注释

// 非递归先序遍历

public static void preorderTraversal(TreeNode root) {

// 用来暂存节点的栈

Stack treeNodeStack = new Stack();

// 新建一个游标节点为根节点

TreeNode node = root;

// 当遍历到最后一个节点的时候,无论它的左右子树都为空,并且栈也为空

// 所以,只要不同时满足这两点,都需要进入循环

while (node != null || !treeNodeStack.isEmpty()) {

// 若当前考查节点非空,则输出该节点的值

// 由考查顺序得知,需要一直往左走

while (node != null) {

System.out.print(node.val + " ");

// 为了之后能找到该节点的右子树,暂存该节点

treeNodeStack.push(node);

node = node.left;

}

// 一直到左子树为空,则开始考虑右子树

// 如果栈已空,就不需要再考虑

// 弹出栈顶元素,将游标等于该节点的右子树

if (!treeNodeStack.isEmpty()) {

node = treeNodeStack.pop();

node = node.right;

}

}

}

另外一种好理解

public void preOrderTraverse2(TreeNode root) {

LinkedList stack = new LinkedList<>();

TreeNode pNode = root;

while (pNode != null || !stack.isEmpty()) {

if (pNode != null) {

System.out.print(pNode.val+" ");

stack.push(pNode);

pNode = pNode.left;

} else { //pNode == null && !stack.isEmpty()

TreeNode node = stack.pop();

pNode = node.right;

}

}

}

2.3 先序遍历结果递归先序遍历: 1 2 4 6 7 8 3 5

非递归先序遍历:1 2 4 6 7 8 3 5

3. 中序遍历

3.1 递归中序遍历

过程和递归先序遍历类似

// 递归中序遍历

public static void recursionMiddleorderTraversal(TreeNode root){

if (root != null) {

recursionMiddleorderTraversal(root.left);

System.out.print(root.val + " ");

recursionMiddleorderTraversal(root.right);

}

}

3.2 非递归中序遍历

和非递归先序遍历类似,唯一区别是考查到当前节点时,并不直接输出该节点。

而是当考查节点为空时,从栈中弹出的时候再进行输出(永远先考虑左子树,直到左子树为空才访问根节点)。

// 非递归中序遍历

public static void middleorderTraversal(TreeNode root) {

Stack treeNodeStack = new Stack();

TreeNode node = root;

while (node != null || !treeNodeStack.isEmpty()) {

while (node != null) {

treeNodeStack.push(node);

node = node.left;

}

if (!treeNodeStack.isEmpty()) {

node = treeNodeStack.pop();

System.out.print(node.val + " ");

node = node.right;

}

}

}

另外一种好理解

public void inOrderTraverse2(TreeNode root) {

LinkedList stack = new LinkedList<>();

TreeNode pNode = root;

while (pNode != null || !stack.isEmpty()) {

if (pNode != null) {

stack.push(pNode);

pNode = pNode.left;

} else { //pNode == null && !stack.isEmpty()

TreeNode node = stack.pop();

System.out.print(node.val+" ");

pNode = node.right;

}

}

}

3.3 中序遍历结果递归中序遍历: 4 7 6 8 2 1 3 5

非递归中序遍历:4 7 6 8 2 1 3 5

4. 后序遍历

4.1 递归后序遍历

过程和递归先序遍历类似

// 递归后序遍历

public static void recursionPostorderTraversal(TreeNode root){

if (root != null) {

recursionPostorderTraversal(root.left);

recursionPostorderTraversal(root.right);

System.out.print(root.val + " ");

}

}

4.2 非递归后序遍历

后续遍历和先序、中序遍历不太一样。

后序遍历在决定是否可以输出当前节点的值的时候,需要考虑其左右子树是否都已经遍历完成。

所以需要设置一个lastVisit游标。

若lastVisit等于当前考查节点的右子树,表示该节点的左右子树都已经遍历完成,则可以输出当前节点。

并把lastVisit节点设置成当前节点,将当前游标节点node设置为空,下一轮就可以访问栈顶元素。

否者,需要接着考虑右子树,node = node.right。

以下考虑后序遍历中的三种情况:

如图所示,从节点1开始考查直到节点4的左子树为空。

注:此时的游标节点node = 4.left == null。

此时需要从栈中查看 Peek()栈顶元素。

发现节点4的右子树非空,需要接着考查右子树,4不能输出,node = node.right。

如图所示,考查到节点7(7.left == null,7是从栈中弹出),其左右子树都为空,可以直接输出7。

此时需要把lastVisit设置成节点7,并把游标节点node设置成null,下一轮循环的时候会考查栈中的节点6。

如图所示,考查完节点8之后(lastVisit == 节点8),将游标节点node赋值为栈顶元素6,节点6的右子树正好等于节点8。表示节点6的左右子树都已经遍历完成,直接输出6。

此时,可以将节点直接从栈中弹出Pop(),之前用的只是Peek()。

将游标节点node设置成null。

// 非递归后序遍历

public static void postorderTraversal(TreeNode root) {

Stack treeNodeStack = new Stack();

TreeNode node = root;

TreeNode lastVisit = root;

while (node != null || !treeNodeStack.isEmpty()) {

while (node != null) {

treeNodeStack.push(node);

node = node.left;

}

//查看当前栈顶元素

node = treeNodeStack.peek();

//如果其右子树也为空,或者右子树已经访问

//则可以直接输出当前节点的值

if (node.right == null || node.right == lastVisit) {

System.out.print(node.val + " ");

treeNodeStack.pop();

lastVisit = node;

node = null;

} else {

//否则,继续遍历右子树

node = node.right;

}

}

}

4.3 后序遍历结果递归后序遍历: 7 8 6 4 2 5 3 1

非递归后序遍历:7 8 6 4 2 5 3 1

5. 层级遍历

层次遍历的代码比较简单,只需要一个队列即可,先在队列中加入根结点。之后对于任意一个结点来说,在其出队列的时候,访问之。同时如果左孩子和右孩子有不为空的,入队列。代码如下:

public void levelTraverse(TreeNode root) {

if (root == null) {

return;

}

LinkedList queue = new LinkedList<>();

queue.offer(root);

while (!queue.isEmpty()) {

TreeNode node = queue.poll();

System.out.print(node.val+" ");

if (node.left != null) {

queue.offer(node.left);

}

if (node.right != null) {

queue.offer(node.right);

}

}

}

6. 深度优先遍历(DFS)

其实深度遍历就是上面的先序、中序和后序。但是为了保证与广度优先遍历相照应,也写在这。代码也比较好理解,其实就是先序遍历,代码如下:

前序遍历是深度优先遍历的一种。

但二叉树深度优先遍历还包括中序遍历、后续遍历。

数据结构:栈

public void depthOrderTraverse(TreeNode root) {

if (root == null) {

return;

}

LinkedList stack = new LinkedList<>();

stack.push(root);

while (!stack.isEmpty()) {

TreeNode node = stack.pop();

System.out.print(node.val+" ");

if (node.right != null) {

stack.push(node.right);

}

if (node.left != null) {

stack.push(node.left);

}

}

}

7. 广度优先遍历(BFS)

数据结构:队列

父节点入队,父节点出队列,先左子节点入队,后右子节点入队。递归遍历全部节点即可

public void levelOrderTraversal(){

if(root==null){

System.out.println("empty tree");

return;

}

ArrayDeque queue=new ArrayDeque();

queue.add(root);

while(queue.isEmpty()==false){

TreeNode node=queue.remove();

System.out.print(node.value+" ");

if(node.left!=null){

queue.add(node.left);

}

if(node.right!=null){

queue.add(node.right);

}

}

System.out.print("\n");

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值