二叉树的四种遍历方式(递归+非递归)

关于二叉树的大多数算法题目都是与二叉树的遍历方式有关的,大都解决方案对二叉树的遍历改写即可。下面就主要介绍下二叉树的常用的四种遍历方式。

其中DFS(Depth-First-Search)的有三种分别为先序遍历,中序遍历及后序遍历,而使用BFS的只有层序遍历。

一.先序遍历

先序遍历的顺序为对于当前结点先访问其本身,再访问其左子树,最后访问其右子树。

递归实现由于过于简单就只给代码不加描述了,

     public static void recurrentSelution(TreeNode root) {
         if (root == null) {
             return;
         }
         System.out.print(root.val + " ");
         recurrentSelution(root.left);
         recurrentSelution(root.right);
     }

非递归实现大体思路:

需要借助一个堆栈,首先将头结点入栈。

若堆栈不为空:

       从堆栈弹出一个元素进行访问;

       若其右孩子存在,则将右孩子入栈;

       若其左孩子存在,则将左孩子入栈。

如此即可完成非递归的先序遍历。

代码如下:

     public static void nonRecurrentSelution(TreeNode root) {
         Stack<TreeNode> stack = new Stack<>();
         stack.push(root);
         while (!stack.isEmpty()) {
             TreeNode current = stack.pop();
             System.out.print(current.val + " ");
             if (current.right != null) {
                 stack.push(current.right);
             }
             if (current.left != null) {
                 stack.push(current.left);
             }
             
         }
     }

二.中序遍历

中序遍历的顺序为对于每一个结点先访问左子树,在访问当前结点,在访问右子树。对于二叉搜索树而言中序遍历的结果即为排序输入的结果。

递归实现代码:

     public static void inOrderRecurr(TreeNode root) {
         if (root == null) {
             return ;
         }
         inOrderRecurr(root.left);
         System.out.print(root.val + " ");
         inOrderRecurr(root.right);
     }

非递归实现:

首先申请一个堆栈,并令当前结点等于根结点。

若堆栈不为空或当前结点不为空时:

       将当前结点及其左孩子以及左孩子的左孩子....依次入栈,直到左孩子为空为止;

       从堆栈中弹出一个元素并访问它;

       令当前元素等于上一步弹出元素的右孩子。

如此即可完成先序遍历的非递归实现

实现代码如下:

     public static void nonInOrderRecurr(TreeNode root) {
         Stack<TreeNode> stack = new Stack<>();
         TreeNode current = root;
         while (current != null || !stack.isEmpty()) {
             while (current != null) {
                 stack.push(current);
                 current = stack.peek().left;
             }
             current = stack.pop();
             System.out.print(current.val + " ");
             current = current.right;
         }
     }

三.后序遍历

后序遍历的顺序为先访问当前结点的左子树,再访问当前节点的右子树,最后访问当前结点。

递归实现代码如下:

     public static void postOrderRecurr(TreeNode root) {
         if (root == null) {
             return ;
         }
         postOrderRecurr(root.left);
         postOrderRecurr(root.right);
         System.out.print(root.val + " ");
     }

非递归实现:

细心地小伙伴可能已经发现了,后序遍历的逆序跟先序遍历类似,后序遍历为左,右,中,而先序遍历为中,左,右因此可以对先序遍历的代码进行稍微的改写完成中,右,左的遍历顺序,然后再将该方式逆序即可。

在先序的非递归遍历中,我们是先让右孩子入栈,再将左孩子入栈的;将其改写为先让左孩子入栈,再让右孩子入栈。同时访问的时候将其先存到另一堆栈,最后将存入新堆栈的元素依次倒入访问即可。

实现代码如下:

     public static void postOrderNonRecurr(TreeNode root) {
         Stack<TreeNode> stack1 = new Stack<>();
         Stack<TreeNode> stack2 = new Stack<>();
         stack1.push(root);
         while (!stack1.isEmpty()) {
             TreeNode current = stack1.pop();
             stack2.push(current);
             if (current.left != null) {
                 stack1.push(current.left);
             }
             if(current.right != null) {
                 stack1.push(current.right);
             }
         }
         while(!stack2.isEmpty()) {
             System.out.print(stack2.pop().val + " ");
         }
     }

四.层序遍历

层序遍历,顾名思义一层一层的遍历啊。从上至下,从左至右依次遍历。

层序遍历的非递归实现:

首先创建一个队列,并将头结点入队

若队列不为空时:

       从队列中弹出一个元素,并访问他;

       若其左孩子存在则将左孩子入队;

       若其右孩子存在则将右孩子入队。

如此即可完成对于队列的层序遍历。

实现代码如下:

     public static void levelOrderTraversal(TreeNode root) {
         Queue<TreeNode> queue = new LinkedList<>();
         queue.add(root);
         while (!queue.isEmpty()) {
             TreeNode current = queue.remove();
             if (current.left != null) {
                 queue.add(current.left);
             }
             if (current.right != null) {
                 queue.add(current.right);
             }
             System.out.print(current.val + " ");
         }
     }

对于层序遍历更进一步的要求是在遍历过程中需要输出换行,使得同一层元素保持的同一行。

对于该问题使用双指针的方案解决。

初始化时定义另个结点,currentLast和nextLast,分别维持当前层的最后一个结点及下一层的最后一个结点。currentLast = root,nextLast = null。

在访问结点的时候若其等于currentLast时,输出换行,currentLast = nextLast

在每一个结点入队时,将nextLast指向入队结点。这样就可以保证在访问到当前层最后一个结点时,此时的nextLast指向的是下一层的最后一个结点。

实现代码如下:

     public static void levelOrderTraversal(TreeNode root) {
         Queue<TreeNode> queue = new LinkedList<>();
         TreeNode last = root;
         TreeNode nLast = root.right == null ? root.left : root.right;
         queue.add(root);
         while (!queue.isEmpty()) {
             TreeNode current = queue.remove();
             if (current.left != null) {
                 queue.add(current.left);
                 nLast = current.left;
             }
             if (current.right != null) {
                 queue.add(current.right);
                 nLast = current.right;
             }
             System.out.print(current.val + " ");
             if (current == last) {
                System.out.println();
                last = nLast;
             }
         }
     }

 

发布了38 篇原创文章 · 获赞 4 · 访问量 2136

猜你喜欢

转载自blog.csdn.net/gw_9527/article/details/103651403