数据结构 —— 四种树的遍历方式|8月更文挑战

509 阅读3分钟

这是我参与8月更文挑战的第29天,活动详情查看:8月更文挑战

树(Tree) 是一个分层的数据结构,由节点和连接节点的边组成,是一种特殊的图,它与图最大的区别是没有循环。树的结构十分直观,而树的很多概念定义都有一个相同的特点:递归。

各种树解决的问题以及面临的新问题:

  • 二叉查找树(BST) :解决了排序的基本问题,但是由于无法保证平衡,可能退化为链表
  • 平衡二叉树(AVL) :通过旋转解决了平衡的问题,但是旋转操作效率太低
  • 红黑树:通过舍弃严格的平衡和引入红黑节点,解决了AVL旋转效率过低的问题,但是在磁盘等场景下,树仍然太高,IO次数太多
  • B树:通过将二叉树改为多路平衡查找树,解决了树过高的问题
  • B+树:在B树的基础上,将非叶节点改造为不存储数据的纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效

树的遍历

前序遍历(Preorder Traversal)

实现原理先访问根节点,然后访问左子树,最后访问右子树

应用场景:运用最多的场合包括在树里进行搜索以及创建一棵新的树。

前序遍历.gif

 // 递归实现
 public void preOrderTraverse1(TreeNode root) {
         if (root != null) {
             System.out.print(root.val + "->");
             preOrderTraverse1(root.left);
             preOrderTraverse1(root.right);
         }
 }
 ​
 // 非递归实现
 public void preOrderTraverse2(TreeNode root) {
         Stack<TreeNode> stack = new Stack<>();
         TreeNode node = root;
         while (node != null || !stack.empty()) {
             if (node != null) {
                 System.out.print(node.val + "->");
                 stack.push(node);
                 node = node.left;
             } else {
                 TreeNode tem = stack.pop();
                 node = tem.right;
             }
         }
 }
中序遍历(Inorder Traversal)

实现原理先访问左子树,然后访问根节点,最后访问右子树

应用场景:最常见的是二叉搜索树,由于二叉搜索树的性质就是左孩子小于根节点,根节点小于右孩子,对二叉搜索树进行中序遍历的时候,被访问到的节点大小是按顺序进行的。

中序遍历.gif

 // 递归实现
 public void inOrderTraverse(TreeNode root) {
         if (root != null) {
             inOrderTraverse(root.left);
             System.out.print(root.val + "->");
             inOrderTraverse(root.right);
         }
 }
 ​
 // 非递归实现
 public void inOrderTraverse(TreeNode root) {
         Stack<TreeNode> stack = new Stack<>();
         TreeNode node = root;
         while (node != null || !stack.isEmpty()) {
             if (node != null) {
                 stack.push(node);
                 node = node.left;
             } else {
                 TreeNode tem = stack.pop();
                 System.out.print(tem.val + "->");
                 node = tem.right;
             }
         }
 }
后序遍历(Postorder Traversal)

实现原理先访问左子树,然后访问右子树,最后访问根节点

应用场景:在对某个节点进行分析的时候,需要来自左子树和右子树的信息。收集信息的操作是从树的底部不断地往上进行,好比你在修剪一棵树的叶子,修剪的方法是从外面不断地往根部将叶子一片片地修剪掉。

后序遍历.gif

 // 递归实现
 public void postOrderTraverse(TreeNode root) {
         if (root != null) {
             postOrderTraverse(root.left);
             postOrderTraverse(root.right);
             System.out.print(root.val + "->");
         }
 }
 ​
 // 非递归实现
 public void postOrderTraverse(TreeNode root) {
         TreeNode cur, pre = null;
 ​
         Stack<TreeNode> stack = new Stack<>();
         stack.push(root);
 ​
         while (!stack.empty()) {
             cur = stack.peek();
             if ((cur.left == null && cur.right == null) || (pre != null && (pre == cur.left || pre == cur.right))) {
                 System.out.print(cur.val + "->");
                 stack.pop();
                 pre = cur;
             } else {
                 if (cur.right != null)
                     stack.push(cur.right);
                 if (cur.left != null)
                     stack.push(cur.left);
             }
         }
 }
层次遍历

除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

 public void levelOrderTraverse(TreeNode root) {
         if (root == null) {
             return;
         }
     
         Queue<TreeNode> queue = new LinkedList<TreeNode>();
         queue.add(root);
         while (!queue.isEmpty()) {
             TreeNode node = queue.poll();
             System.out.print(node.val + "->");
 ​
             if (node.left != null) {
                 queue.add(node.left);
             }
             if (node.right != null) {
                 queue.add(node.right);
             }
         }
 }