二叉树的遍历
二叉树——是一种 典型的非线性 数据结构。 而 在计算机程序中,遍历本身是一种线性操作。因此,二叉树的遍历,本质是非线性关联的节点转换成 一个线性 的序列,再 进行线性遍历。
遍历类型
-
深度遍历
- 前序遍历(根-> 左-> 右)
- 中序遍历( 左->根-> 右)
- 后续遍历( 左-> 右->根)
-
广度遍历
- 层序遍历(一层一层的 遍历)
实例
二叉树 :
二叉树节点
public class TreeNode {
//数据内容
public int data;
//左孩子节点
public TreeNode leftChild;
//右孩子节点
public TreeNode rightChild;
public TreeNode(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public TreeNode getLeftChild() {
return leftChild;
}
public void setLeftChild(TreeNode leftChild) {
this.leftChild = leftChild;
}
public TreeNode getRightChild() {
return rightChild;
}
public void setRightChild(TreeNode rightChild) {
this.rightChild = rightChild;
}
@Override
public String toString() {
return "TreeNode{" +
"data=" + data +
'}';
}
}
构建实例中的二叉树
public static TreeNode createBinaryTree(){
TreeNode root=new TreeNode(3);
root.leftChild=new TreeNode(2);
root.leftChild.leftChild=new TreeNode(9);
root.leftChild.rightChild=new TreeNode(7);
root.rightChild=new TreeNode(8);
root.rightChild.rightChild=new TreeNode(4);
return root;
}
深度遍历
- 递归法
/**
* 递归前序遍历
* @param root
*/
public static void preOrderTraval(TreeNode root){
if(root!=null){
//根
System.out.print(" 递归前序->"+root.data );
//左
preOrderTraval(root.leftChild);
//右
preOrderTraval(root.rightChild);
}
}
/**
* 递归中序遍历
* @param root
*/
public static void middleOrderTraveral(TreeNode root){
if(root!=null){
//左
middleOrderTraveral(root.leftChild);
//根
System.out.print(" 递归中序->"+root.data);
//右
middleOrderTraveral(root.rightChild);
}
}
/**
* 递归后续遍历
* @param root
*/
public static void postOrderTraveral(TreeNode root){
if(root!=null){
//左
postOrderTraveral(root.leftChild);
//右
postOrderTraveral(root.rightChild);
//根
System.out.print(" 递归后续->"+root.data);
}
}
- 迭代法
- 前序遍历
/**
* 迭代前序遍历方法一
*
* @param root
*/
public static void preOrderTraveralByStackA(TreeNode root) {
//用栈来回溯节点路径
Stack<TreeNode> treeNodeStack = new Stack<>();
while (root != null || !treeNodeStack.isEmpty()) {
//当节点不为null的时候,一直压入栈中(从根节点沿着 左节点一头扎到底)
while (root != null) {
treeNodeStack.push(root);
System.out.println("迭代前序 " + root.data);
root = root.leftChild;
}
//当节点为null的时候,出栈,获取null 的父节点
TreeNode stackTopNode = treeNodeStack.pop();
//root切换到 父节点的右节点
root = stackTopNode.rightChild;
}
}
/**
* 跌打前序遍历方法二: 由于我们遍历顺序 是 根,左,右,结合栈的出栈规则,我们应该按照 根 ,右 ,左 压入栈中。
*
* @param root
*/
public static void preOrderTraveralByStackB(TreeNode root) {
Stack<TreeNode> treeNodeStack = new Stack<>();
//压入根节点
treeNodeStack.push(root);
while (!treeNodeStack.isEmpty()) {
//出栈
TreeNode popNode = treeNodeStack.pop();
System.out.println(" 迭代前序B->" + popNode.data);
if (popNode.rightChild != null) {
treeNodeStack.push(popNode.rightChild);
}
if (popNode.leftChild != null) {
treeNodeStack.push(popNode.leftChild);
}
}
}
- 中序遍历
/**
* 迭代中序遍历
*
* @param root
*/
public static void middleOrderTraveralByStack(TreeNode root) {
//用栈来回溯节点路径
Stack<TreeNode> treeNodeStack = new Stack<>();
while (root != null || !treeNodeStack.isEmpty()) {
//当节点不为null的时候,一直压入栈中(从根节点沿着 左节点一头扎到底)
while (root != null) {
treeNodeStack.push(root);
root = root.leftChild;
}
//当节点为null的时候,出栈,获取null 的父节点
TreeNode stackTopNode = treeNodeStack.pop();
System.out.println("迭代中序 " + stackTopNode.data);
//root切换到 父节点的右节点
root = stackTopNode.rightChild;
}
}
3.后续遍历
/**
* 迭代 后续遍历
* @param root
*/
public static void postOrderTraveralByStackA(TreeNode root){
//用栈来回溯节点路径
Stack<TreeNode> treeNodeStack = new Stack<>();
//上一个访问的node
TreeNode preNode=null;
while (root != null || !treeNodeStack.isEmpty()) {
//当节点不为null的时候,一直压入栈中(从根节点沿着 左节点一头扎到底)
while (root != null) {
treeNodeStack.push(root);
root = root.leftChild;
}
//当节点为左节点null的时候,获取null 的父节点,此时 我们要访问 父节点的 右节点,所以父节点并没有出栈
//因此会导致1 个问题:如果父节点 的右节点不为null时,需要入栈,从而导致,父节点一定会出现第二次被访问。所以需要设置标志控制
TreeNode stackTopNode = treeNodeStack.peek();
if(stackTopNode.rightChild==null||stackTopNode.rightChild==preNode){
//如果该节点的右节点为null 或者 stackTopNode节点已经被访问过了(因为它的右节点和)
//真正出栈
treeNodeStack.pop();
System.out.println(" 后续遍历-》 "+stackTopNode.data);
root=null;
}else{
//切换到右节点
root=stackTopNode.rightChild;
}
preNode=stackTopNode;
}
}
/**
* 迭代后续遍历双栈法
* 思路: 后续遍历(左, 右, 根) 可以 由 前序遍历(根 ,左 右) 转换而来。
* 因此,前序遍历 转换 成————》右,左,根 ————反转(通过栈)
* @param root
*/
public static void postOrderTraveralByDoubleStack(TreeNode root){
Stack<TreeNode> treeNodeStack=new Stack<>();
Stack<TreeNode> reserveStack=new Stack<>();
treeNodeStack.push(root);
while (!treeNodeStack.isEmpty()){
TreeNode popNode = treeNodeStack.pop();
reserveStack.push(popNode);
//由于需要 ,先右,后左,所以。先将左压入栈
if(popNode.leftChild!=null){
treeNodeStack.push(root.leftChild);
}
if(popNode.rightChild!=null){
treeNodeStack.push(root.rightChild);
}
}
//输出
while (!reserveStack.isEmpty()){
TreeNode pop = reserveStack.pop();
System.out.println(" 双栈法-》" +pop.data);
}
}
广度遍历
/**
* 广度遍历
* @param root
*/
public static void levelOrderTraversal(TreeNode root){
LinkedList<TreeNode> deque=new LinkedList<>();
deque.add(root);
while (!deque.isEmpty()){
TreeNode pop = deque.poll();
System.out.println("广度遍历 "+ pop.data);
if(pop.leftChild!=null){
deque.add(pop.leftChild);
}
if(pop.rightChild!=null){
deque.add(pop.rightChild);
}
}
}