树遍历算法概述
树的遍历
遍历定义——指按某条搜索路线遍访每个结点且不重复(又称周游)
遍历用途——它是树结构插入、删除、修改、查找和排序运算的前提,是二叉树一切运算的基础和核心
遍历方法——牢记一种约定,对每个结点的查看都是“先左后右”
树的遍历有两个基本的方法:深度优先遍历 和 广度优先遍历 。
深度优先遍历又根据处理节点的顺序不同,可以分为:中序遍历、前序遍历和后序遍历。这些知识点也是深度优先遍历经常考察的。
广度优先遍历的考察在于层次遍历
实例:
- 先序遍历:ABCDEFGH
- 中序遍历:BDCEAFHG
- 后序遍历:DECBHGFA
树遍历的口诀
- DLR—先序遍历,即先根再左再右
- LDR—中序遍历,即先左再根再右
- LRD—后序遍历,即先左再右再根
简单讲就是根节点先、中、后的顺序。
树的递归遍历代码
public class BiTree { String val; BiTree left; BiTree right; BiTree(String x) { val = x; } }
树的先序遍历
public void preOrder(BiTree root) { if (root == null) { return; } System.out.print(root.val + " "); preOrder(root.left); preOrder(root.right); }
树的中序遍历
public void inOrder(BiTree root) { if (root == null) { return; } inOrder(root.left); System.out.print(root.val + " "); inOrder(root.right); }
树的后序遍历
public void postOrder(BiTree root) { if (root == null) { return; } postOrder(root.left); postOrder(root.right); System.out.print(root.val + " "); }
测试:
/** * A * / \ * B F * / \ * C G * / \ / * D E H */ @Test public void testOrder() { BiTree tn = new BiTree("A"); tn.left = new BiTree("B"); tn.left.right = new BiTree("C"); tn.left.right.left = new BiTree("D"); tn.left.right.right = new BiTree("E"); tn.right = new BiTree("F"); tn.right.right = new BiTree("G"); tn.right.right.left = new BiTree("H"); System.out.print("递归前序遍历:"); preOrder(tn); System.out.println(); System.out.print("递归中序遍历:"); inOrder(tn); System.out.println(); System.out.print("递归后序遍历:"); postOrder(tn); System.out.println(); System.out.print("非递归前序遍历:"); preOrderTraversal(tn); System.out.println(); System.out.print("非递归中序遍历:"); midOrderTraversal(tn); System.out.println(); System.out.print("非递归后序遍历:"); postOrderTraversal(tn); System.out.println(); System.out.print("非递归广度遍历:"); levelOrderTraversal(tn); System.out.println(); }
递归遍历思想
从前面的三种遍历算法可以知道:如果将print语句抹去,从递归的角度看,这三种算法是完全相同的,或者说这三种遍历算法的访问路径是相同的,只是访问结点的时机不同
从虚线的出发点到终点的路径上,每个结点经过3次
第1次经过时访问=先序遍历
第2次经过时访问=中序遍历
第3次经过时访问=后序遍历
二叉树遍历的时间效率和空间效率
时间效率:O(n) //每个结点只访问一次
空间效率:O(n) //栈占用的最大辅助空间(精确值:树深为k的递归遍历需要k+1个辅助单元!)
树的非递归遍历
1、广度优先遍历-层次遍历
优先遍历-层次遍历实现的方式,使用了队列的FIFO的方式,将所有暂未访问的节点存入一个队列结构,先进根节点,然后循环:出队列头,然后分别进左,右子树节点。如此反复,直至队列为空。
如下代码是经典的广度遍历
/** * 广度优先遍历,非递归实现,需要辅助数据结构:队列 * @param root */ public void levelOrderTraversal(BiTree root) { if (root == null) { System.out.println("empty tree"); return; } ArrayDeque<BiTree> queue = new ArrayDeque<BiTree>(); queue.add(root); while (queue.isEmpty() == false) { BiTree node = queue.remove(); System.out.print(node.val + " "); if (node.left != null) { queue.add(node.left); } if (node.right != null) { queue.add(node.right); } } System.out.print("\n"); }
结果:A B F C G D E H
2、树非递归的先序遍历
算法思想:
/** * 非递归-前序遍历 * * @param root */ public void preOrderTraversal(BiTree root) { if (root == null) return; Stack<BiTree> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { BiTree node = stack.pop(); System.out.print(node.val + " "); if (node.right != null) { stack.push(node.right); } if (node.left != null) { stack.push(node.left); } } }
中序遍历:
/** * 非递归-中序 * * @param root */ public void midOrderTraversal(BiTree root) { if (root == null) return; Stack<BiTree> stack = new Stack<>(); while (root != null || !stack.isEmpty()) { if (root != null) { stack.push(root); root = root.left; } else { BiTree node = stack.pop(); System.out.print(node.val + " "); root = node.right; } } }
后序遍历:
/** * 非递归-后序 * @param root */ public void postOrderTraversal(BiTree root) { if (root == null) return; Stack<BiTree> stack1 = new Stack<>(); Stack<BiTree> stack2 = new Stack<>(); stack1.push(root); while (!stack1.isEmpty()) { root = stack1.pop(); stack2.push(root); if (root.left != null) { stack1.push(root.left); } if (root.right != null) { stack1.push(root.right); } } while (!stack2.isEmpty()) { System.out.print(stack2.pop().val + " "); } }
结果:
递归前序遍历:A B C D E F G H
递归中序遍历:B D C E A F H G
递归后序遍历:D E C B H G F A
非递归前序遍历:A B C D E F G H
非递归中序遍历:B D C E A F H G
非递归后序遍历:D E C B H G F A
非递归广度遍历:A B F C G D E H
全部代码:
import org.junit.Test; import java.util.ArrayDeque; import java.util.Stack; public class Tree { public class BiTree { String val; BiTree left; BiTree right; BiTree(String x) { val = x; } } public void preOrder(BiTree root) { if (root == null) { return; } System.out.print(root.val + " "); preOrder(root.left); preOrder(root.right); } public void inOrder(BiTree root) { if (root == null) { return; } inOrder(root.left); System.out.print(root.val + " "); inOrder(root.right); } public void postOrder(BiTree root) { if (root == null) { return; } postOrder(root.left); postOrder(root.right); System.out.print(root.val + " "); } /** * A * / \ * B F * / \ * C G * / \ / * D E H */ @Test public void testOrder() { BiTree tn = new BiTree("A"); tn.left = new BiTree("B"); tn.left.right = new BiTree("C"); tn.left.right.left = new BiTree("D"); tn.left.right.right = new BiTree("E"); tn.right = new BiTree("F"); tn.right.right = new BiTree("G"); tn.right.right.left = new BiTree("H"); System.out.print("递归前序遍历:"); preOrder(tn); System.out.println(); System.out.print("递归中序遍历:"); inOrder(tn); System.out.println(); System.out.print("递归后序遍历:"); postOrder(tn); System.out.println(); System.out.print("非递归前序遍历:"); preOrderTraversal(tn); System.out.println(); System.out.print("非递归中序遍历:"); midOrderTraversal(tn); System.out.println(); System.out.print("非递归后序遍历:"); postOrderTraversal(tn); System.out.println(); System.out.print("非递归广度遍历:"); levelOrderTraversal(tn); System.out.println(); } /** * 非递归-前序遍历 * * @param root */ public void preOrderTraversal(BiTree root) { if (root == null) return; Stack<BiTree> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { BiTree node = stack.pop(); System.out.print(node.val + " "); if (node.right != null) { stack.push(node.right); } if (node.left != null) { stack.push(node.left); } } } /** * 非递归-中序 * * @param root */ public void midOrderTraversal(BiTree root) { if (root == null) return; Stack<BiTree> stack = new Stack<>(); while (root != null || !stack.isEmpty()) { if (root != null) { stack.push(root); root = root.left; } else { BiTree node = stack.pop(); System.out.print(node.val + " "); root = node.right; } } } /** * 非递归-后序 * @param root */ public void postOrderTraversal(BiTree root) { if (root == null) return; Stack<BiTree> stack1 = new Stack<>(); Stack<BiTree> stack2 = new Stack<>(); stack1.push(root); while (!stack1.isEmpty()) { root = stack1.pop(); stack2.push(root); if (root.left != null) { stack1.push(root.left); } if (root.right != null) { stack1.push(root.right); } } while (!stack2.isEmpty()) { System.out.print(stack2.pop().val + " "); } } /** * 广度优先遍历,非递归实现,需要辅助数据结构:队列 * * @param root */ public void levelOrderTraversal(BiTree root) { if (root == null) { System.out.println("empty tree"); return; } ArrayDeque<BiTree> queue = new ArrayDeque<BiTree>(); queue.add(root); while (queue.isEmpty() == false) { BiTree node = queue.remove(); System.out.print(node.val + " "); if (node.left != null) { queue.add(node.left); } if (node.right != null) { queue.add(node.right); } } System.out.print("\n"); } }
参考:https://blog.csdn.net/Erice_s/article/details/78375302?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_utm_term~default-0.pc_relevant_paycolumn_v3&spm=1001.2101.3001.4242.1&utm_relevant_index=3