Loading

二叉树的基本知识

二叉树的四种遍历方式

不要较真,其实也可以分为两种:广度优先(层级)和深度优先(前序、中序、后序)

基本概念不再赘述。复杂度:设二叉树中元素数目为n。这四种遍历算法的空间复杂性均为O (n),时间复杂性为O(n)。

二叉树数据结构

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;
        }
    }
      1
     / \
    2   5
   / \     
  3   4   

前序遍历

遍历顺序:根节点-> 左节点-> 右节点

代码实现:

/**
     * 前序遍历  根 -> 左 -> 右
     */
    public void preOrder(TreeNode tree){
        if (tree == null){
            return;
        }
        System.out.print(tree.val);
        preOrder(tree.left);
        preOrder(tree.right);
    }

非递归方式

//java 中使用 Deque, Stack已经弃用。
    //Deque 的使用用法:push、pop。
    public void perOrderIter(TreeNode root){
        if (root == null){
            return;
        }
        Deque<TreeNode> stack = new ArrayDeque<>();
        StringBuilder result = new StringBuilder();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode treeNode = stack.pop();
            result.append(treeNode.val);
            if (treeNode.right != null){
                stack.push(treeNode.right);
            }
            if (treeNode.left != null){
                stack.push(treeNode.left);
            }
        }
        System.out.println(result.toString());
    }

中序遍历

遍历顺序:左节点-> 根节点-> 右节点

代码实现

/**
 * 中序遍历  左 -> 根 -> 右
 * 结果:32415
 */
public void midOrder(TreeNode tree){
    if (tree == null){
        return;
    }
    midOrder(tree.left);
    System.out.print(tree.val);
    midOrder(tree.right);
}
/**
     * 迭代式中序遍历  左 -> 根 -> 右
     * 这个比较难,重点关注一下。
     */
    public void minOrderIter(TreeNode root){
        if (root == null){
            return;
        }
        Deque<TreeNode> stack = new ArrayDeque<>();
        StringBuilder result = new StringBuilder();
        while(root != null || !stack.isEmpty()){

            //此处的目的是放入将根节点放入,然后将根节点的左节点压在根节点上面。
            while (root != null){
                stack.push(root);
                root = root.left;
            }
            //调出栈
            root = stack.pop();
            result.append(root.val);
            root = root.right;
        }
        System.out.println(result.toString());
    }

后序遍历

遍历顺序:左节点-> 右节点-> 根节点

/**
     * 后序遍历  左 -> 右 -> 根
     * 结果:34251
     */
    public void postOrder(TreeNode tree){
        if (tree == null){
            return;
        }
        postOrder(tree.left);
        postOrder(tree.right);
        System.out.print(tree.val);
    }
/**
     * 迭代式后序遍历
     * 后序遍历更复杂!!!!
     * 先遍历左节点 -> 右节点 -> 根节点
     *      1
     *      / \
     *     2   5
     *    / \
     *   3   4
     *      / \
     *     7   8
     */
    public void postOrderIter(TreeNode root){
        if (root == null){
            return;
        }
        Deque<TreeNode> stack = new ArrayDeque<>();
        StringBuilder result = new StringBuilder();
        TreeNode pre = null; //记录前置节点
        while(root != null || !stack.isEmpty()){
            //把所有的左子树节点都放入栈中
            while(root != null){
                stack.push(root);
                root = root.left;
            }

            //找到当前节点
            root = stack.pop();
            //如果当前节点的右节点为空
            //这里为什么会有对pre的判断,是为了避免重复处理。
            //拿例子:当8已经处理完了之后,应该处理4节点,当时发现4也是有右子树的,但是8已经处理过了,通过pre达标,那么8也不用处理。
            if (root.right == null || pre == root.right){
                result.append(root.val);
                //设置前置节点
                pre = root;
                //置为空的目的是处理栈中堆积的父节点。
                root = null;
            } else{
                //右节点非空,说明当前节点这个时候不能够处理,就把当前节点再放回去。
                stack.push(root);
                //把当前节点的右节点作为root进行处理。
                root = root.right;
            }
        }
        System.out.println(result.toString());
    }

层级遍历

/**
 * 层级遍历
 * 递归的方式
 * 递归需要存储每个的层级 对应的数据都有什么,借助额外的数据结构
 */
public List<StringBuilder> result = new ArrayList<>();

public void levelOrder(TreeNode root, int level) {
    if (root == null) {
        return;
    }
    //当数组大小等于层级时,初始化该层级需要的存储空间
    if (result.size() == level) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(root.val);
        result.add(level, stringBuilder);
    } else {
        result.get(level).append(root.val);
    }
    levelOrder(root.left, level + 1);
    levelOrder(root.right, level + 1);
}
/**
 * 迭代式层级遍历
 * 借助额外的数据结构:队列,特性:先进先出
 * queue 的基本用法:add(offer),remove(poll)
 */
public void levelOrderIter(TreeNode root) {
    if (root == null) {
        return;
    }
    StringBuilder result = new StringBuilder();
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    while(!queue.isEmpty()){
        TreeNode current = queue.poll();
        result.append(current.val);
        if (current.left != null){
            queue.add(current.left);
        }
        if (current.right != null){
            queue.add(current.right);
        }
    }
    System.out.println(result.toString());
}

额外:

/**
 * 获取二叉树的最大深度
 */
public int getMaxDepth(TreeNode root){
    if (root == null){
        return 0;
    }
    return Math.max(getMaxDepth(root.left)+1,getMaxDepth(root.right)+1);
}

文章作者: 冯廷鑫
文章链接: http://fengtingxin.github.io/2022/06/12/二叉树的基本知识/

posted @ 2022-06-13 00:53  冯廷鑫  阅读(91)  评论(0编辑  收藏  举报