Java 实现 二叉树的 前序遍历

Posted 龙凌云端

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 实现 二叉树的 前序遍历相关的知识,希望对你有一定的参考价值。

Java 实现 二叉树的 前序遍历


 

class Node 
    int val;
    Node left;
    Node right;
    Node(int val) 
        this.val = val;
    


public class BinaryTree 
    /**
     * 前序遍历
     * @param root 节点
     */
    public void preorderTraversal(Node root) 
        if (root != null) 
            System.out.print(root.val + " ");
            preorderTraversal(root.left);
            preorderTraversal(root.right);
        
    

 

解析:
首先,定义了一个 Node 类表示二叉树的节点。节点包含了一个整型的 val 值,以及左右两个子节点的引用。
然后,定义了 BinaryTree 类,该类包含了一个方法 preorderTraversal,用于实现前序遍历。
前序遍历的顺序是:先遍历根节点,然后遍历左子树,最后遍历右子树。
如果二叉树为空,则直接返回。否则,先输出根节点的值,然后递归遍历左子树和右子树。

使用方法:
首先创建二叉树的节点,然后构建二叉树,最后调用 BinaryTree 类的 preorderTraversal 方法进行前序遍历。
 
测试代码如下:
public class TestTree 
    public static void main(String[] args) 
        // 构建二叉树
        Node root = new Node(1);
        root.left = new Node(2);
        root.right = new Node(3);
        root.left.left = new Node(4);
        root.left.right = new Node(5);
        // 先序遍历
        BinaryTree bt = new BinaryTree();
        System.out.print("Preorder Traversal: ");
        bt.preorderTraversal(root); // 1 2 4 5 3

 

 

PS

Java 实现 二叉树的 前序遍历https://www.cnblogs.com/miracle-luna/p/17368605.html
Java 实现 二叉树的 中序遍历https://www.cnblogs.com/miracle-luna/p/17368610.html
Java 实现 二叉树的 后序遍历https://www.cnblogs.com/miracle-luna/p/17368606.html
 
 

基于Java的二叉树的三种遍历方式的递归与非递归实现

二叉树的遍历方式包括前序遍历中序遍历后序遍历,其实现方式包括递归实现非递归实现

前序遍历:根节点 | 左子树 | 右子树

中序遍历:左子树 | 根节点 | 右子树

后序遍历:左子树 | 右子树 | 根节点

1. 递归实现

递归方式实现代码十分简洁,三种遍历方式的递归实现代码结构相同,只是执行顺序有所区别。

前序遍历:

public class preOrderRecur {
List<Integer> res = new ArrayList<>();
public List<Integer> preOrderTraversal(TreeNode root) {
if (root != null) {
res.add(root.val); // 根节点
preOrderTraversal(root.left); // 左子树
preOrderTraversal(root.right); // 右子树
}
return res;
}
}

中序遍历:

public class inOrderRecur {
List<Integer> res = new ArrayList<>();
public List<Integer> inOrderTraversal(TreeNode root) {
if (root != null) {
inOrderTraversal(root.left); // 左子树
res.add(root.val); // 根节点
inOrderTraversal(root.right); // 右子树
}
return res;
}
}

后序遍历:

public class inOrderRecur {
List<Integer> res = new ArrayList<>();
public List<Integer> inOrderTraversal(TreeNode root) {
if (root != null) {
inOrderTraversal(root.left); // 左子树
inOrderTraversal(root.right); // 右子树
res.add(root.val); // 根节点
}
return res;
}
}

2. 迭代实现

2.1 使用辅助栈——空间复杂度O(N)

2.1.1 中序遍历
  • 从当前结点一直向其最左孩子搜索,直到没有左孩子了停止,这个过程中将路程中的所有结点入栈;

  • 弹出栈顶元素,将其记录在答案中,并把当前结点置为弹出元素的右孩子并重复第一步过程。

public class inOrderIterator {
List<Integer> res = new ArrayList<>();
public List<Integer> inOrderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
if (root != null) {
stack.push(root);
root = root.left;
} else {
TreeNode node = stack.pop();
res.add(node.val);
root = node.right;
}
}
return res;
}
}
2.1.2 前序遍历

方法1:因为前序遍历访问顺序是“中-左-右”,所以可以先将根结点压栈,然后按照下列步骤执行。

  • 如果栈不为空,则弹出栈顶元素存入结果中;

  • 如果弹出元素的右孩子不为空则将右孩子压栈,然后如果其左孩子也不为空将其左孩子压栈(因为栈是后入先出的,所以为了达到“中-左-右”的顺序,需要先压入右孩子,再压入左孩子)。

public class preOrderIterator {
List<Integer> res = new ArrayList<>();
public List<Integer> inOrderTraversal(TreeNode root) {
if (root == null) return res;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
root = stack.pop();
res.add(root.val);
// 右孩子压栈
if (root.right != null) stack.push(root.right);
// 左孩子压栈
if (root.left != null) stack.push(root.left);
}
return res;
}
}

方法2:根据中序遍历进行微调:

public class preOrderIterator {
List<Integer> res = new ArrayList<>();
public List<Integer> inOrderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
if (root != null) {
res.add(root.val);
stack.push(root);
root = root.left;
} else {
TreeNode node = stack.pop();
root = node.right;
}
}
return res;
}
}
2.1.3 后序遍历

因为前序遍历的顺序是“左-中-右”,而后序遍历顺序是“左-右-中”,不考虑左结点,区别只是在于中结点和右结点的顺序进行了反向而已,因此可以使用前序遍历的代码进行调整,只需要将前序遍历对左右孩子压栈的顺序反向即可,即先压入左孩子,再压入右孩子。除此之外,因为按照这种方法调整得到的遍历顺序为“中-右-左”,正好是后序遍历的反向顺序,因此在获得遍历序列后还需进行逆序操作。

public class postOrderIterator {
List<Integer> res = new LinkedList<>();
public List<Integer> postOrderTraversal(TreeNode root) {
if (root == null) return res;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
root = stack.pop();
// 头插法
res.add(0, root.val);
// 左孩子压栈
if (root.left != null) stack.push(root.left);
// 右孩子压栈
if (root.right != null) stack.push(root.right);
}
return res;
}
}

2.2 Morris遍历——空间复杂度O(1)

该方法的思路简单说就是,对于每一个结点,找到它左孩子的最右子结点,因为按照正常访问顺序,其左孩子的最有子节点访问完后就应该访问其本身了,因此将其左孩子最右子节点的右指针指向它。基本步骤如下:

  • 如果当前结点左孩子为空,说明最左边访问完毕,将其置为其右孩子

  • 如果当前结点左孩子不为空,那么开始尝试找到该结点左孩子的最右子节点,建立连接关系

    • 如果找到的当前结点的左孩子的最右子节点右指针为空,说明还未建立连接关系,是首次访问当前结点,那么将该最右结点的右指针指向当前结点,然后当前结点向左孩子走一步继续重复所有步骤。

    • 如果找到的当前结点的左孩子的最右子节点右指针不为空,说明已建立过连接关系,是第二次访问当前结点,这意味着当前结点的左子树应该已经全部遍历完了,此时应恢复连接关系重新置为空,然后当前结点向右孩子走一步继续重复所有步骤。

该方法虽然保证了O(1)的空间复杂度,但在遍历过程中改变了部分结点的指向,破坏了树的结构。

2.2.1 中序遍历
public class inOrderMorris {
List<Integer> res = new ArrayList<>();
public List<Integer> inOrderTraversal(TreeNode root) {
TreeNode pre = null;
TreeNode cur = root;
while (cur != null) {
if (cur.left == null) {
res.add(cur.val);
cur = cur.right;
} else {
pre = cur.left;
while (pre.right != null && pre.right != cur) pre = pre.right;
if (pre.right == null) {
pre.right = cur;
cur = cur.left;
} else {
res.add(cur.val);
pre.right = null;
cur = cur.right;
}
}
}
return res;
}
}
2.2.2 前序遍历
public class preOrderMorris {
List<Integer> res = new ArrayList<>();
public List<Integer> preOrderTraversal(TreeNode root) {
TreeNode pre = null;
TreeNode cur = root;
while (cur != null) {
if (cur.left == null) {
res.add(cur.val);
cur = cur.right;
} else {
pre = cur.left;
while (pre.right != null && pre.right != cur) pre = pre.right;
if (pre.right == null) {
res.add(cur.val);
pre.right = cur;
cur = cur.left;
} else {
pre.right = null;
cur = cur.right;
}
}
}
return res;
}
}
2.2.3 后序遍历

前序遍历反向的思想

public class postOrderMorris {
List<Integer> res = new LinkedList<>();
public List<Integer> postOrderTraversal(TreeNode root) {
TreeNode pre = null;
TreeNode cur = root;
while (cur != null) {
if (cur.right == null) {
res.add(0, cur.val);
cur = cur.left;
} else {
pre = cur.right;
while (pre.left != null && pre.left != cur) pre = pre.left;
if (pre.left == null) {
res.add(0, cur.val);
pre.left = cur;
cur = cur.right;
} else {
pre.left = null;
cur = cur.left;
}
}
}
return res;
}
}


以上是关于Java 实现 二叉树的 前序遍历的主要内容,如果未能解决你的问题,请参考以下文章

(Java)二叉树的三种遍历(非递归实现)

144_二叉树的前序遍历

树二叉树遍历算法(深度优先广度优先遍历,前序中序后序层次)及Java实现

基于Java的二叉树的三种遍历方式的递归与非递归实现

二叉树的前序中序和后续遍历及应用场景

Java实现二叉树的前序中序后序层序遍历(递归方法)