Java实现二叉树2

在阅读本文之前,建议读者优先阅读专栏内前面的文章。

目录

前言

一、二叉树OJ题1:相同的树

二、二叉树OJ题2:另一棵树的子树

三、二叉树OJ题3:翻转二叉树

四、二叉树OJ题4:对称二叉树

五、二叉树OJ题5:判断一颗二叉树是否是平衡二叉树

六、二叉树OJ题6:二叉搜索树与双向链表

七、二叉树OJ题7:二叉树的构建及遍历

八、二叉树OJ题8:二叉树的最近公共祖先

九、二叉树OJ题9:前序遍历和中序遍历构造二叉树

十、二叉树OJ题10:中序遍历和后序遍历构造二叉树

十一、二叉树OJ题11:二叉树构建字符串

十二、二叉树OJ题12:非递归实现前序遍历

十三、二叉树OJ题13:非递归实现中序遍历

十四、二叉树OJ题14:非递归实现后序遍历

总结


前言

本文主要介绍用Java实现一系列与二叉树有关的OJ题。


一、二叉树OJ题1:相同的树

题目测试链接如下:100. 相同的树 - 力扣(LeetCode)

在题目中已经明确说明,如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。那么我们为了有条理,逻辑清晰地去解决这个题目,最好按照一定的次序去排除。我们可以先判断结构是否一致,若结构一致,再判断对应节点的值是否一致。若值也一致,就可以判断左子树和右子树是否一致了。我这里给出我的代码:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if((p != null && q == null) || (p == null && q != null)){
            return false;
        }

        if(p == null && q == null){
            return true;
        }

        if(p.val != q.val){
            return false;
        }

        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}

二、二叉树OJ题2:另一棵树的子树

题目测试链接如下:572. 另一棵树的子树 - 力扣(LeetCode)

这道题其实如果借用上一道题的代码就会比较容易实现了。我们可以先去比较当前子树根节点是否与根节点相同,如果不同就去分别和左子树根节点和右子树根节点比较即可。读者可以思考如何实现,我这里给出我的代码:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(root == null){
            return false;
        }
        if(isSameTree(root, subRoot)){
            return true;
        }
        return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
    }
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if((p != null && q == null) || (p == null && q != null)){
            return false;
        }

        if(p == null && q == null){
            return true;
        }

        if(p.val != q.val){
            return false;
        }

        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}

三、二叉树OJ题3:翻转二叉树

题目测试链接如下:226. 翻转二叉树 - 力扣(LeetCode)

这道题其实核心就是遍历节点然后交换节点的左右子树就可以了,相对来说是比较简单的。读者可以先思考一下如何实现,我这里给出我的代码:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null){
            return root;
        }

        if(root.left == null && root.right == null){
            return root;
        }
        
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

四、二叉树OJ题4:对称二叉树

题目测试链接如下:101. 对称二叉树 - 力扣(LeetCode)

这道题的思路就是首先把判断整棵树是否对称转化为判断左子树和右子树是否对称,那么我们如何去判断这个问题呢?还是先判断二者的结构是否一样,再判断二者的根值是否一样。如果都一样的话,我们就再去判断左子树的左树和右子树的右树是否一样以及左子树的右树和右子树的左树是否一样。读者可以思考一下如何实现,我这里给出我的代码:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if((root.left == null && root.right != null) || (root.left != null && root.right == null)){
            return false;
        }
        return isSymmetric(root.left, root.right);
    }
    public boolean isSymmetric(TreeNode p, TreeNode q) {
        if(p == null && q == null){
            return true;
        }
        if((p.left == null && q.right != null) || (p.left != null && q.right == null) || (p.right == null && q.left != null) || (p.right != null && q.left == null)){
            return false;
        }
        if(p.val != q.val){
            return false;
        }
        return isSymmetric(p.left, q.right) && isSymmetric(p.right, q.left);
    }
}

上面的方法,我们是用了方法的重载,但是感觉很麻烦,所以干脆直接用一个新方法就好了:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null){
            return true;
        }
        return isSymmetricChild(root.left, root.right);
    }
    public boolean isSymmetricChild(TreeNode leftTree, TreeNode rightTree) {
        if((leftTree == null && rightTree != null) || (rightTree == null && leftTree != null)){
            return false;
        }
        if(leftTree == null && rightTree == null){
            return true;
        }
        if(leftTree.val != rightTree.val){
            return false;
        }
        return isSymmetricChild(leftTree.left, rightTree.right) && isSymmetricChild(leftTree.right, rightTree.left);
    }
}

其实,我们也可以用上面的已有的方法来解决,我们其实可以直接反转根的右子树,然后看下是否和左子树相同就可以。我的代码如下:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        return isSameTree(root.left, invertTree(root.right));
    }
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if((p != null && q == null) || (p == null && q != null)){
            return false;
        }

        if(p == null && q == null){
            return true;
        }

        if(p.val != q.val){
            return false;
        }

        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
    public TreeNode invertTree(TreeNode root) {
        if(root == null){
            return root;
        }

        if(root.left == null && root.right == null){
            return root;
        }
        
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

五、二叉树OJ题5:判断一颗二叉树是否是平衡二叉树

题目测试链接如下:110. 平衡二叉树 - 力扣(LeetCode)

在了解之后,我们发现,二叉平衡树其实就是要求每节点的左右子树高度之差必须小于等于1.那么这样的话,其实思路就比较明确了,就是我们遍历每个节点求左右子树高度,如果大于2就直接返回false即可。请读者思考如何实现,我这里给出我的代码:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null){
            return true;
        }
        return Math.abs(getHeight(root.left) - getHeight(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
    }

    public int getHeight(TreeNode root){
        if(root == null){
            return 0;
        }
        return Math.max(getHeight(root.left), getHeight(root.right)) + 1;
    }
}

这个方法虽然能过,但是时间复杂度达到了O(n²),因为我们相当于在递归中嵌套了递归,所以并不是最佳的方案。为了提高代码的效率,我们就需要找到重复计算的位置,然后进行优化。很明显,在我们从上到下计算高度的时候,第一层算过后,第二层理论上直接减1即可,但是我们由于嵌套递归,所以出现大量重复的计算。那么这里就是我们优化的部分,如果当前子树是平衡的,就返回它的实际高度;如果当前子树已经不平衡,就返回-1作为标记。这样在递归过程中,一旦发现某棵子树不平衡,就可以立即向上返回-1,不再继续进行无意义的高度计算,从而起到剪枝作用。由于每个节点在整个过程中最多只会被访问一次,因此时间复杂度由O(n²)优化为O(n)。

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null){
            return true;
        }
        return getHeight(root) >= 0;
    }

    public int getHeight(TreeNode root){
        if(root == null){
            return 0;
        }
        int leftHeight = getHeight(root.left);
        if(leftHeight < 0){
            return -1;
        }
        int rightHeight = getHeight(root.right);
        if(rightHeight >= 0 && Math.abs(leftHeight - rightHeight) <= 1){
            return Math.max(leftHeight, rightHeight) + 1;
        }else{
            return -1;
        }
    }
}

六、二叉树OJ题6:二叉搜索树与双向链表

题目测试链接如下:二叉搜索树与双向链表_牛客题霸_牛客网

我们的核心思路是利用二叉搜索树中序遍历结果有序的特点,将二叉搜索树原地转换为一个有序的双向链表。由于二叉搜索树按照中序遍历可以得到从小到大的节点序列,因此在递归过程中按照左子树、当前节点、右子树的顺序访问每一个节点,并使用一个全局变量prev记录当前节点的前一个节点。每访问到一个节点时,就将当前节点的left指针指向prev,表示当前节点的前驱;如果prev不为空,则将prev的right指针指向当前节点,表示前一个节点的后继。随后将prev更新为当前节点,继续处理右子树。这样在中序遍历的过程中,所有节点就会按照升序顺序被依次连接成双向链表。由于原来的根节点不一定是链表的头节点,因此转换完成后需要从原根节点开始不断沿着left指针向前寻找,直到找到最左端的节点,也就是链表中的最小节点,并将其作为双向链表的头节点返回。整个过程没有创建新的节点,只是修改了原二叉树节点的左右指针,时间复杂度为O(n),空间复杂度取决于递归栈深度,为O(h)。我的代码如下:

java 复制代码
import java.util.*;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    TreeNode prev = null;
    public void ConvertChild(TreeNode root) {
        if(root == null){
            return;
        }
        ConvertChild(root.left);
        
        root.left = prev;
        if(prev != null){
            prev.right = root;
        }
        prev = root;
        ConvertChild(root.right);
    }
    
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null){
            return null;
        }
        ConvertChild(pRootOfTree);
        TreeNode head = pRootOfTree;
        while(head.left != null){
            head = head.left;
        }
        return head;
    }
}

七、二叉树OJ题7:二叉树的构建及遍历

题目测试链接如下:二叉树遍历_牛客题霸_牛客网

我的思路是输入字符串中使用#表示空节点,因此在构建二叉树时,可以按照先序遍历根节点、左子树、右子树的顺序递归处理字符串。程序使用一个全局下标i指向当前正在读取的字符,如果当前字符不是#,说明该位置是一个真实节点,就根据该字符创建新的TreeNode节点,然后下标后移,继续递归构建它的左子树和右子树;如果当前字符是#,说明当前位置为空节点,只需要将下标后移并返回null。这样递归结束后,就可以得到完整的二叉树结构。构建完成后,程序再调用中序遍历方法,按照左子树、根节点、右子树的顺序访问节点并输出节点值。我的代码如下:

java 复制代码
import java.util.Scanner;

class TreeNode{
    public char val;
    public TreeNode left;
    public TreeNode right;

    public TreeNode(char val){
        this.val = val;
    }
}
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextLine()) { // 注意 while 处理多个 case
            String str = in.nextLine();
            TreeNode root = createTree(str);
            inOrderTree(root);
        }
    }
    public static int i = 0;
    public static TreeNode createTree(String str){
        TreeNode root = null;
        if(str.charAt(i) != '#'){
            root = new TreeNode(str.charAt(i));
            i++;
            root.left = createTree(str);
            root.right = createTree(str);
        }else{
            i++;
        }

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

八、二叉树OJ题8:二叉树的最近公共祖先

题目测试链接如下:236. 二叉树的最近公共祖先 - 力扣(LeetCode)

我们可以采用递归的后序遍历思想,从根节点开始向左右子树分别查找节点p和节点q。首先,如果当前节点为空,说明这条路径上没有找到目标节点,直接返回null;如果当前节点正好等于p或q,说明已经找到了其中一个目标节点,直接返回当前节点。接着递归查找当前节点的左子树和右子树,分别用leftTree和rightTree保存查找结果。如果左子树和右子树都返回非空,说明p和q分别位于当前节点的两侧,那么当前节点就是它们的最近公共祖先;如果只有左子树返回非空,说明两个目标节点都在左子树中,或者只找到了其中一个节点,因此继续向上返回leftTree;如果左子树为空,则返回右子树的查找结果。整个过程通过递归返回值不断向上传递目标节点或公共祖先,最终得到p和q的最近公共祖先。我的代码如下:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null){
            return null;
        }
        if(root == p || root == q){
            return root;
        }

        TreeNode leftTree = lowestCommonAncestor(root.left, p, q);
        TreeNode rightTree = lowestCommonAncestor(root.right, p, q);

        if(leftTree != null && rightTree != null){
            return root;
        }else if(leftTree != null){
            return leftTree;
        }else{
            return rightTree;
        }
    }
}

九、二叉树OJ题9:前序遍历和中序遍历构造二叉树

题目测试链接如下:236. 二叉树的最近公共祖先 - 力扣(LeetCode)

我们知道前序遍历的访问顺序为根节点、左子树、右子树,因此每次递归时,前序遍历当前指针所指向的元素一定是当前子树的根节点。程序使用全局变量preIndex记录当前根节点在前序遍历数组中的位置,根据该位置创建根节点后,再到中序遍历数组中查找该节点的位置。由于中序遍历的顺序为左子树、根节点、右子树,因此根节点左侧的部分属于当前节点的左子树,右侧的部分属于当前节点的右子树,从而可以将问题划分为左右两个更小的子问题。随后递归构建左子树和右子树,每构建一个节点就将preIndex后移,使其始终指向下一棵子树的根节点。当中序遍历区间为空时,说明当前子树不存在,递归结束返回null。整个过程不断利用前序遍历确定根节点,利用中序遍历划分左右子树,最终完成整棵二叉树的重建。我的代码如下:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int preIndex;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        
        return buildTreeChild(preorder, inorder, 0, inorder.length - 1);
    }

    public TreeNode buildTreeChild(int[] preorder, int[] inorder, int inbegin, int inend) {
        
        if(inbegin > inend){
            return null;
        }

        TreeNode root = new TreeNode(preorder[preIndex]);

        int rootIndex = findVal(inorder, inbegin, inend, preorder[preIndex]);

        preIndex++;

        root.left = buildTreeChild(preorder, inorder, inbegin, rootIndex - 1);
        root.right = buildTreeChild(preorder, inorder, rootIndex + 1, inend);

        return root;
    }
    
    private int findVal(int[] inorder, int inbegin, int inend, int val){
        for(int i = inbegin; i <= inend; i++){
            if(inorder[i] == val){
                return i;
            }
        }
        return -1;
    }
}

十、二叉树OJ题10:中序遍历和后序遍历构造二叉树

题目测试链接如下:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

后序遍历的顺序是左子树、右子树、根节点,因此每次递归时,后序数组当前指针postIndex所指向的元素一定是当前子树的根节点。程序从后序遍历的末尾开始向前取值,依次确定每一棵子树的根节点。确定根节点后,在中序遍历数组中找到该节点的位置,由于中序遍历遵循左子树、根节点、右子树的结构,因此该位置左侧属于左子树,右侧属于右子树,从而可以将当前问题划分为左右两个子问题。与前序+中序不同的是,这里必须先递归构建右子树,再构建左子树,因为后序遍历是从右往左逆序使用的。每确定一个节点就将postIndex减一,使其始终指向当前子树的根节点。递归在中序区间无效(inbegin > inend)时结束,表示当前子树为空。整个过程通过后序遍历确定根节点,通过中序遍历划分左右子树,不断递归完成整棵二叉树的重建。我的代码如下:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int postIndex;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        postIndex = postorder.length - 1;
        return buildTreeChild(inorder, postorder, 0, inorder.length - 1);
    }
    public TreeNode buildTreeChild(int[] inorder, int[] postorder, int inbegin, int inend) {
        
        if(inbegin > inend){
            return null;
        }

        TreeNode root = new TreeNode(postorder[postIndex]);

        int rootIndex = findVal(inorder, inbegin, inend, postorder[postIndex]);

        postIndex--;

        root.right = buildTreeChild(inorder, postorder, rootIndex + 1, inend);
        root.left = buildTreeChild(inorder, postorder, inbegin, rootIndex - 1);

        return root;
    }
    
    private int findVal(int[] inorder, int inbegin, int inend, int val){
        for(int i = inbegin; i <= inend; i++){
            if(inorder[i] == val){
                return i;
            }
        }
        return -1;
    }
}

十一、二叉树OJ题11:二叉树构建字符串

题目测试链接如下:606. 根据二叉树创建字符串 - 力扣(LeetCode)

对于这道题,我的思路是对于每一个节点,首先将当前节点的值追加到字符串中,然后分别处理其左右子树。如果当前节点存在左子树,则在左子树前添加一对括号,并递归构建左子树的字符串;如果左子树为空但右子树不为空,则必须添加空括号来保证结构的唯一性,避免歧义。随后再处理右子树,如果右子树存在,则同样在其前后添加括号并递归构建;如果右子树为空,则可以直接跳过不处理。整个过程通过递归不断展开树结构,并利用括号明确表示子树的层级关系,从而生成一个可以唯一还原原二叉树结构的字符串表示。我的代码如下:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    public String tree2str(TreeNode root) {
        if(root == null){
            return null;
        }
        StringBuilder stringBuilder = new StringBuilder();
        tree2strChild(root, stringBuilder);
        return stringBuilder.toString();
    }
    public void tree2strChild(TreeNode t, StringBuilder stringBuilder){
        if(t == null){
            return;
        }
        stringBuilder.append(t.val);
        if(t.left != null){
            stringBuilder.append("(");
            tree2strChild(t.left, stringBuilder);
            stringBuilder.append(")");
        }else{
            if(t.right == null){
                return;
            }else{
                stringBuilder.append("()");
            }
        }
        if(t.right != null){
            stringBuilder.append("(");
            tree2strChild(t.right, stringBuilder);
            stringBuilder.append(")");
        }else{
            return;
        }
    }
}

十二、二叉树OJ题12:非递归实现前序遍历

题目测试链接如下:144. 二叉树的前序遍历 - 力扣(LeetCode)

我的思路是采用栈的方式模拟递归过程。前序遍历的顺序是根节点 -> 左子树 -> 右子树,因此在遍历过程中,当访问到一个节点时,首先将该节点的值加入结果列表,然后尽可能一直向左子树深入,同时将经过的节点依次压入栈中,以便之后回溯处理右子树。当当前节点为空时,说明已经到达左侧尽头,此时从栈中弹出一个节点,并将指针移动到该节点的右子树,继续重复上述过程。整个循环通过"当前节点不为空或栈不为空"作为终止条件,保证所有节点都被访问一次。我的代码如下:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> list = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        if(root == null){
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                list.add(cur.val);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            cur = top.right;
        }
        return list;
    }
}

十三、二叉树OJ题13:非递归实现中序遍历

题目测试链接如下:94. 二叉树的中序遍历 - 力扣(LeetCode)

在有了前面这道题的实现过程之后,本题实现相对就容易些了。读者可以自行实现,我这里给出我的代码:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> list = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        if(root == null){
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            list.add(top.val);
            cur = top.right;
        }
        return list;
    }
}

十四、二叉树OJ题14:非递归实现后序遍历

题目测试链接如下:145. 二叉树的后序遍历 - 力扣(LeetCode)

有了上面两道题的基础,相信读者可以很快写出代码了,我的代码如下:

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;

        while(cur != null || !stack.isEmpty()){

            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }

            TreeNode top = stack.peek();

            // 右子树为空 或 已经访问过右子树
            if(top.right == null || top.right == prev){
                list.add(top.val);
                stack.pop();
                prev = top;
            }else{
                cur = top.right;
            }
        }

        return list;
    }
}

总结

本文介绍了使用Java实现二叉树相关OJ题的解法,涵盖了多个经典题目。主要内容包括:判断两棵二叉树是否相同、判断子树、翻转二叉树、对称二叉树、平衡二叉树、二叉搜索树转双向链表、二叉树构建及遍历、最近公共祖先、根据遍历序列构造二叉树、二叉树字符串表示以及非递归实现前序/中序/后序遍历等。每个题目都提供了详细的思路分析和完整的Java代码实现,其中特别针对递归和迭代方法进行了对比和优化。文章通过14个具体例题,系统地讲解了二叉树的基本操作和常见算法,适合需要巩固二叉树相关算法知识的读者参考学习。