数据结构(十一)二叉树—— 二叉树的节点遍历

为了方便我们的讨论,在学习二叉树节点的遍历方式之前,我们创建一个二叉树的结构,以此来讨论二叉树的各种遍历方式

二叉树的各种方式的遍历,是一个既简单又麻烦的问题。简单在于,找到规律后我们会发现:如何遍历整个二叉树结构,如何遍历二叉树的子树结构,这个过程直接使用递归结构就能够完成;麻烦的是,我们需要找到规律……

一、广度优先遍历

对一棵二叉树进行广度优先遍历,就是对一棵二叉树中所有的节点,按照层次从上到下、每一层中节点从左到右的顺序,将二叉树中的所有节点进行遍历的操作。广度优先遍历方式只有一种,那就是程序遍历,而二叉树的程序遍历操作,需要依赖于队列结构。

使用队列对二叉树进行层序遍历的步骤如下:
步骤1:将二叉树的根节点加入队列
步骤2:将队列头节点出队列
步骤3.1:如果根节点具有左孩子,则根节点的左孩子入队列
步骤3.2:如果根节点具有右孩子,则根节点的右孩子入队列
然后重复上述的步骤2至步骤3.2,直至整个队列中不再具有元素为止。在步骤3.1至步骤3.2中,一定是左孩子先进入队列,然后才是右孩子进入队列。

实际上广度优先遍历是一种比较简单的操作方式,主要是利用了队列先进先出的数据结构特点,在从二叉树顶端的树根开始加入队列的时候开始,逐层向下,李荣二叉树只能就行二分的规则,不断的将子节点按照从左到右的顺序进行加入,最终在队列清空的情况下,得到完成的二叉树自上到下、从左到右的节点遍历顺序。

二、深度优先遍历

深度优先遍历是二叉树遍历方式中比较重点的一种遍历方式,可以分为先序遍历、中序遍历和后序遍历3种遍历序列。和广度优先遍历方式不同的是,深度优先遍历是从根节点开始,一条路走到黑,一个分支一个分支的进行节点遍历,其中一个分支全部遍历结束之后,再去进行下一个分支的遍历。在深度优先遍历中,我们必须记住这样一个规则:如何遍历整个二叉树,就如何遍历左右子树。

1、先序遍历

先序遍历处理节点的顺序是:根左右,即先访问根节点,然后访问左子树,最后访问右子树。先序遍历二叉树节点的步骤是:

步骤1:首先得到整个二叉树的根节点,并访问这个根节点
步骤2:如果根节点具有左孩子,则将左孩子及其子节点作为一个单独的二叉树进行先序遍历
步骤3:在遍历过程中,如果一个节点没有左孩子或者左孩子已经遍历完成,那么判断这个节点有没有右孩子
步骤4:如果这个节点具有右孩子,则将右孩子及其子节点作为一个单独的二叉树进行先序遍历
步骤5:如果这个节点的没有右孩子或者右孩子已经遍历完成,那么向上回溯到父节点,重复判断父节点是否具有右孩子
步骤6:重复上述步2­5,直到最终回溯到根节点为止,表示根节点及其左右子树已经全部遍历完成,得到完整的先序遍历序列

2、中序遍历

中序遍历处理节点的顺序是:左根右,即先访问左子树,然后访问根节点,最后访问右子树中序遍历二叉树节点的步骤是:

步骤1:在得到根节点的时候,首先不要对根节点进行访问,而是直接判断这个根节点有没有左孩子
步骤2:如果根节点存在左孩子,则对以左孩子为根的左子树进行中序遍历
步骤3:如果一个节点不存在左孩子,或者左孩子已经遍历完成,那么项父节点进行回溯,在回溯到父节点的时候,访问父节点
步骤4:在父节点访问完成后,判断父节点是否具有右孩子
步骤5:如果父节点具有右孩子,则对右孩子为根的右子树进行中序遍历
步骤6:如果父节点不具有右孩子或者右子树已经遍历完成,那么向上回溯的父节点
步骤7:重复上述步骤1­6,直到回溯到整个二叉树的根节点为止,遍历完成,得到完整的中序遍历序列

3.后序遍历

后序遍历处理节点的顺序是:左右根,即先访问左子树,然后访问右子树,最后访问根节点后序遍历二叉树节点的步骤是:

步骤1:得到整个二叉树的根节点,但是并不访问根节点,判断根节点是否具有左孩子
步骤2:如果根节点具有左孩子,则后序遍历以左孩子为根的左子树
步骤3:如果根节点不具有左孩子,或者左子树遍历完成,则回溯到根节点,但是依然不访
问根节点,而是判断根节点是否具有右孩子
步骤4:如果根节点具有右孩子,则后序遍历以右孩子为根的右子树
步骤5:如果根节点不具有右孩子,或者右子树遍历完成,则回溯到根节点,此时访问根节点
步骤6:重复上述步骤1­-5,直到回溯到整个二叉树的根节点并且根节点访问完毕为止

4.规律总结
从上面进行的先序、中序、后序三种遍历操作中我们不难得出如下一些规律,这些规律在后序的通过遍历序列反推二叉树结构的过程中会使用到:
规律1:在先序序列中,最先出现的节点一定是整棵树的根节点
规律2:在后序序列中,最后出现的节点一定是整棵树的根节点
规律3:在中序序列中,整棵树的根节点出现在序列的中间
规律4:在中序序列中,根节点左边的节点都是左子树的构成节点;根节点右边的节点都是右子树的构成节点。

三、代码实现

import java.util.LinkedList;

public class BinaryTree {
	
	public static class Node {
		
		Object data;  //数据域
		
		Node leftChild;  //左孩子指针域
		Node rightChild;  //右孩子指针域
		
		//为了在创建节点的时候更加方便,我们为节点提供一个能够直接传递数据域数据的构造器
		public Node(Object data) {
			this.data = data;
		}
		
	}
	
	private Node root;  //树根节点,为了方便后面的遍历案例二创建的结构
	
	public Node getRoot() {
		return root;
	}
	
	public BinaryTree() {
		
		//初始化一个具有固定结构的二叉树,二叉树结构见图片
		
		//[1]先把所有的节点定义出来
		Node A = new Node("A");
		Node B = new Node("B");
		Node C = new Node("C");
		Node D = new Node("D");
		Node E = new Node("E");
		Node F = new Node("F");
		Node G = new Node("G");
		Node H = new Node("H");
		Node I = new Node("I");
		Node J = new Node("J");
		
		//[2]根据图示挂载每一个节点的左右孩子节点
		A.leftChild = B;
		A.rightChild = C;
		B.leftChild = D;
		D.leftChild = G;
		D.rightChild = H;
		C.leftChild = E;
		C.rightChild = F;
		E.rightChild = I;
		F.rightChild = J;
		
		//[3]根节点指向A节点
		this.root = A;
		
	}
	
	/**
	 * 层序遍历
	 * @param root 整个二叉树的根节点
	 */
	public void levelOrderTraversal(Node root) {
		
		//[1]创建一个队列结构,用来保存二叉树中的节点,同时也使用这个队列结构来确定层序遍历的节点出入队列顺序
		LinkedList<Node> nodeList = new LinkedList<>();
		
		//[2]首先将整个二叉树的根节点入队列
		nodeList.offer(root);
		
		//[3]创建一个循环,开始对二叉树进行层序遍历
		while(!nodeList.isEmpty()) {  //如果队列中不在具有节点,说明整个二叉树遍历完成
			
			//[4]首先将队列头节点出队列
			Node node = nodeList.poll();
			System.out.print(node.data);
			
			//[5.1]如果队列头结点左孩子不是空,则将左孩子入队列
			if(node.leftChild != null) {
				nodeList.offer(node.leftChild);
			}
			
			//[5.2]如果队列头节点右孩子不是空,则将右孩子入队列
			if(node.rightChild != null) {
				nodeList.offer(node.rightChild);
			}
			
		}
		
	}
	
	/**
	 * 先序遍历
	 * @param root 整个二叉树或者子树的根节点
	 */
	public void preOrderTraversal(Node root) {
		
		//[1]访问根节点
		System.out.print(root.data);
		
		//[2]如果左孩子不为空,则先序遍历左子树
		if(root.leftChild != null) {
			preOrderTraversal(root.leftChild);
		}
		
		//[3]如果右孩子不为空,则先序遍历右子树
		if(root.rightChild != null) {
			preOrderTraversal(root.rightChild);
		}
		
	}
	
	/**
	 * 中序遍历
	 * @param root 整个二叉树或者子树的根节点
	 */
	public void inOrderTraversal(Node root) {
		
		//[1]如果左孩子不为空,则中序遍历左子树
		if(root.leftChild != null) {
			inOrderTraversal(root.leftChild);
		}
		
		//[2]访问根节点
		System.out.print(root.data);
		
		//[3]如果右孩子不为空,则中序遍历右子树
		if(root.rightChild != null) {
			inOrderTraversal(root.rightChild);
		}
		
	}
	
	/**
	 * 后序遍历
	 * @param root 整个二叉树或者子树的根节点
	 */
	public void postOrderTraversal(Node root) {
		
		//[1]如果左孩子胡为空,则后序遍历左子树
		if(root.leftChild != null) {
			postOrderTraversal(root.leftChild);
		}
		
		//[2]如果右孩子不为空,则后序遍历右子树
		if(root.rightChild != null) {
			postOrderTraversal(root.rightChild);
		}
		
		//[3]访问根节点
		System.out.print(root.data);
		
	}
	
}

测试类:

package com.oracle.binarytree;

public class TestBinaryTree {
	
	public static void main(String[] args) {
		
		BinaryTree bt = new BinaryTree();
		
		BinaryTree.Node root = bt.getRoot();
		
		System.out.println("-----层序遍历-----");
		
		bt.levelOrderTraversal(root);
		
		System.out.println();
		
		System.out.println("-----先序遍历-----");
		
		bt.preOrderTraversal(root);
		
		System.out.println();
		
		System.out.println("-----中序遍历-----");
		
		bt.inOrderTraversal(root);
		
		System.out.println();
		
		System.out.println("-----后序遍历-----");
		
		bt.postOrderTraversal(root);
		
		System.out.println();
		
	}
	
}

结果显示:

发布了98 篇原创文章 · 获赞 165 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/cyl101816/article/details/95695190