【数据结构与算法】二叉树的遍历与构造

写在前面

算法部分具体代码在github:GitHub - tod4-lab/Algorithms: Learning record of the book Algorithms(forth edition).,不定时进行学习更新

1 二叉树的遍历

递归写法比较常见,这里直接写非递归写法了。

1.1 二叉树的先序遍历

    public void preOrderIter(TreeNode root) {
        if(root == null)
            return;
        Stack<TreeNode> s = new Stack<>();
        s.push(root);
        while(!s.empty()) {
            TreeNode temp = s.pop();
            System.out.println(temp.val);
            if(temp.right!=null) {
                s.push(temp.right);
            }
            if(temp.left!=null) {
                s.push(temp.left);
            }
        }
    }

非递归写法的大体思想是首先将根节点入栈,然后每次弹出栈顶节点,再将弹出节点的右节点、左节点依次入栈,因为栈先入后出的特性,根节点的左节点会成为栈顶在下一轮先出栈,由此实现根、左、右的先序遍历。

1.2 二叉树的中序遍历

public class InorderIter {
    public static void printInorderIter(BinaryTree.TreeNode root) {
        Stack<BinaryTree.TreeNode> s = new Stack<>();
        while(root!=null) {
            s.push(root);
            root = root.left;
        }
        while(!s.empty()) {
            BinaryTree.TreeNode node = s.pop();
            System.out.println(node.val);
            BinaryTree.TreeNode temp = node.right;
            while(temp!=null) {
                s.push(temp);
                temp = temp.left;
            }
        }
    }
}

中序遍历的非递归写法的大体思想是,先将不断遍历子节点的左节点依次入栈,直到为空则每次先弹出根节点,然后再弹出右节点,再依次遍历子节点的左节点依次入栈。。。以此实现中序序列。

1.3 二叉树的后序遍历

后序遍历可以使用两个栈:一个栈进行根节点、右子树、左子树的顺序进行遍历,另一个栈负责将其逆序则可以得到后序序列。

public class PostOrderIter {
    public static void postOrderIterPrint(BinaryTree.TreeNode root) {
        if(root==null)
            return;
        Stack<BinaryTree.TreeNode> s1 = new Stack();
        Stack<BinaryTree.TreeNode> s2 = new Stack();
        s1.push(root);
        while(!s1.empty()) {
            BinaryTree.TreeNode temp = s1.pop();
            s2.push(temp);
            if(temp.left!=null)
                s1.push(temp.left);
            if(temp.right!=null)
                s1.push(temp.right);
        }
        while(!s2.empty()) {
            System.out.println(s2.pop().val);
        }
    }
}

非常巧妙的算法,据说后序遍历也能够用一个栈实现非递归算法,但是暂时没有想到怎么做,后序想到会回来填坑的

2 二叉树的构建

2.1 根据先序和中序构建二叉树

测试样例:

先序:3,9,20,15,7

中序:9,3,15,20,7

结果:3,9,20,null,null,15,7

二叉树结构:

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

        TreeNode(int x) {
            val = x;
        }
    }
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder.length == 0)
            return null;
        TreeNode root = new TreeNode(preorder[0]);
        int rootIndex = 0;
        while (inorder[rootIndex] != preorder[0]) {
            rootIndex++;
        }
        int len1 = rootIndex;
        int len2 = preorder.length - len1 - 1;

        TreeNode leftTree = null;
        TreeNode rightTree  = null;

        int[] leftPreOrder = new int[len1];
        int[] leftInOrder = new int[len1];
        int[] rightPreOrder = new int[len2];
        int[] rightInOrder = new int[len2];

        if(len1!=0) {
            for (int i = 0; i < len1;i++) {
                leftInOrder[i] = inorder[i];
                leftPreOrder[i] = preorder[i+1];
            }
            leftTree = buildTree(leftPreOrder, leftInOrder);
        }
        if(len2!=0) {
            for (int i = 0; i < len2; i++) {
                rightInOrder[i] = inorder[i + rootIndex + 1];
                rightPreOrder[i] = preorder[i + rootIndex + 1];
            }
            rightTree = buildTree(rightPreOrder, rightInOrder);
        }
        root.left = leftTree;
        root.right = rightTree;
        return root;
    }

代码写的有点乱,大题思路是每次在先序序列中找到根节点并构建,然后根据根节点在中序序列的位置,能够确定左子树节点个数len1、右子树节点个数len2以及左右子树的划分。则递归进行对左右子树的构建,然后将根节点指针指向左右子树。

比较烦人的点就是数组下标的确定:

preOrder:左子树(1,len1) 右子树(len1+1,len1+len2)

inOrder:左子树(0,len1-1)右子树(len1,len1+len2)

posted @ 2022-07-31 12:53  Tod4  阅读(37)  评论(0编辑  收藏  举报