几个二叉树相关的基础算法(广度/深度优先搜索,二叉树最大深度等)

本文介绍了二叉树的基础算法,包括广度优先搜索(BFS)的两种实现,深度优先搜索(DFS)的先序、中序和后序遍历,以及如何计算二叉树的最大宽度、最大深度和叶子节点数量。这些知识对于准备技术笔试和面试至关重要。
摘要由CSDN通过智能技术生成

二叉树的相关算法题无疑是笔试、面试中考察最频繁的知识点了,所以在你准备笔试、面试前一定要对其进行掌握。本篇文章主要介绍几个二叉树相关的基础算法,同样这也是我们务必要理解掌握的,并且要能写出来的。

值得说明的是,看懂这几个算法的实现,你要具备二叉树的一些基础知识,比如:什么是二叉树的广度优先搜索、深度搜索,什么是先序遍历、中序遍历以及后序遍历,什么是二叉树的深度、宽度以及二叉树的叶子节点是怎么定义的。

其次,你最好也具备递归算法的思想,因为二叉树相关的好多算法都运用了递归的思想,所以你要是理解什么是递归,对看懂这篇文章有极大的帮助。

广度优先搜索

二叉树的广度优先搜索(Breadth First Search,BFS)通常是借助队列实现的。

根据返回值类型的不同,这里给了两种形式:

public class BFS {
    // 第一种:以数组的方式返回结果
    public int[] levelOrder1(TreeNode root) {
        // 如果根节点为空,返回空数组
        if (root == null) return new int[0];
        // 声明一个队列,用来存储树的节点
        Queue<TreeNode> queue = new LinkedList<>();
        // 声明一个链表,用来存储节点值
        ArrayList<Integer> ans = new ArrayList<>();
        // 根节点进队列
        queue.add(root);

        // !queue.isEmpty()
        // 但队列不为空时,继续访问队列
        while (queue.size() != 0) {
            // 取出下一个要访问的节点,称为当前节点
            TreeNode curr = queue.poll();
            // 访问当前节点的值,并添加到ans链表中
            ans.add(curr.val);
            // 向队列中添加左右子树节点
            // 先添加左子树
            if (curr.left != null) queue.add(curr.left);
            // 再添加右子树
            if (curr.right != null) queue.add(curr.right);
        }

        // 下面代码的作用是将链表ans的结果传给数组,并返回。
        int size = ans.size();
        int[] result = new int[size];
        for (int i = 0; i < size; i++) {
            result[i] = ans.get(i);
        }
        return result;
    }

    // 第二种:以List集合返回 层先方法
    public List<List<Integer>> levelOrder2(TreeNode root) {
        // 这里先创建一个结果链表
        List<List<Integer>> res = new ArrayList<>();
        if (root == null)  return  res;
        Queue<TreeNode> nodeQueue = new LinkedList<>();
        nodeQueue.add(root);

        while(!nodeQueue.isEmpty()) {
            //这里是每一层创建一个链表,用来存放当前一层的节点值
            ArrayList<Integer> ans = new ArrayList<>();
            // 循环当前队列的所有节点,即为树的该层所有的节点
            for (int i = nodeQueue.size(); i > 0; i--) {
                TreeNode curr = nodeQueue.poll();
                ans.add(curr.val);
                // 这里相当于是把下一层节点放入队列中
                if (curr.left != null) nodeQueue.add(curr.left);
                if (curr.right != null) nodeQueue.add(curr.right);
            }
            // 将存放每一层的节点值的链表添加到结果链表中
            res.add(ans);
        }
        // 直接返回结果
        return res;
    }
}

深度优先搜索

二叉树的深度优先搜索(Depth First Search,DFS)按照不同的遍历顺序可以分为:先序遍历、中序遍历以及后序遍历。

不管那种遍历方式都可以借助来实现。

  • 先序遍历很好理解,代码中给出了详细的备注解释。
  • 中序遍历稍微有点难理解,但是只要你深刻理解了中序遍历的访问顺序,再看代码的实现是不难的。
  • 后序遍历是在先序遍历的基础上,加以修改:
    • 首先,先序遍历是:中左右
    • 然后,调换下顺序:中右左(这个很容易实现,只需要调整左右子树的访问顺序即可)
    • 最后,反转下数组:左右中(这正是后序遍历的顺序)
public class DFS {
    // 1、先序遍历
    public int[] preorderTraversal(TreeNode root) {
        // 如果根节点为空,返回空数组
        if (root == null) return new int[0];
        // 声明一个栈,用来存放各节点
        Stack<TreeNode> stack = new Stack<>();
        // 声明一个链表,用来存放各节点的值
        ArrayList<Integer> ans = new ArrayList<>();
        // 根节点压栈
        stack.push(root);
        // 当栈不为空时,证明树的节点还未访问完,继续访问
        while (!stack.isEmpty()) {
            // 当前节点弹栈
            TreeNode node = stack.pop();
            // 访问当前节点的值,并添加到链表中年
            ans.add(node.val);
            // 先压栈右节点,再压栈左节点
            // 为什么先压右节点,再压左节点?这里自己动手画一画流程图就清楚明白了
            if (node.right != null) stack.push(node.right);
            if (node.left != null) stack.push(node.left);
        }
        // 下面代码的作用是将链表ans的结果传给数组,并返回。
        int size = ans.size();
        int[] result = new int[size];
        for (int i = 0; i < size; i++) {
            result[i] = ans.get(i);
        }
        return result;
    }

    // 2、中序遍历
    public int[] inorderTraversal(TreeNode root) {
        if (root == null) return new int[0];
        Stack<TreeNode> stack = new Stack<>();
        ArrayList<Integer> ans = new ArrayList<>();
        // 中序遍历,需要对当前节点进行判断,所以先声明一个当前节点
        TreeNode curr = root;
        // 如果当前节点不为空,或者栈不为空,都进入循环,为什么?
        // 第一次是因为栈空,但当前节点为root不为空
        // 中间两者都满足,都不为空
        // 当到叶子节点时,当前节点为空,但是栈不为空
        // 直到访问了最后一个叶子节点,两个都为空了,也就结束了
        while (curr != null || !stack.isEmpty()) {
            if (curr != null){
                // 当前节点不为空,压栈
                // 这里是因为中序遍历,所以要先访问左子树的最后一个节点
                // 所以要先压栈,不能访问
                stack.push(curr);
                // 当前节点压栈以后,指向自己的左子树
                curr = curr.left;
            } else {
                // 如果当前节点为空了,证明到了叶子节点,
                // 这时在栈中弹出最顶的节点,即为最左的一个节点
                curr = stack.pop();
                // 访问节点,并将值添加链表中
                ans.add(curr.val);
                // 指向右子树
                curr = curr.right;
            }
        }
        // 下面代码的作用是将链表ans的结果传给数组,并返回。
        int size = ans.size();
        int[] result = new int[size];
        for (int i = 0; i < size; i++) {
            result[i] = ans.get(i);
        }
        return result;
    }

    // 3、后序遍历
    // 明白了先序遍历,后序遍历就简单跟多了
    public int[] postorderTraversal(TreeNode root) {
        if (root == null) return new int[0];
        Stack<TreeNode> stack = new Stack<>();
        ArrayList<Integer> temp = new ArrayList<>();
        stack.push(root);

        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            temp.add(node.val);
            if (node.left != null) stack.push(node.left);
            if (node.right != null) stack.push(node.right);
        }

        int size = temp.size();
        int[] result = new int[size];
        for (int i = 0; i < size; i++) {
            // 反向赋值
            result[i] = temp.get(size - i - 1);
        }
        return result;
    }
}

二叉树的宽度

二叉树的最大宽度可以基于广度优先搜索实现,话不多说,直接上代码:

public class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        // 不说了
        if(root == null) return 0;
        // 用来存放树的节点
        Queue<TreeNode> nodeQueue = new LinkedList<>();
        // 记录最大的宽度
        int width = 0;
        nodeQueue.add(root);
        while (!nodeQueue.isEmpty()) {
            // 重点就是这一句代码
            // 每次对比最大值和该层的宽度,选择两者的最大值
            width = Math.max(nodeQueue.size(), width);
            for (int i = nodeQueue.size(); i > 0; i--) {
                TreeNode curr = nodeQueue.poll();
                // 左右子树添加到队列
                if (curr.left != null) nodeQueue.add(curr.left);
                if (curr.right != null) nodeQueue.add(curr.right);
            }
        }
        return width;
    }
}

二叉树的深度

二叉树的最大深度可以基于递归的思想实现,直接上代码:

public class Solution1 {
    // 利用深度优先  -  迭代法
    public int maxDepth(TreeNode root) {
        // 如果根节点为空,直接返回0
        if (root == null) return 0;
        // 根节点不为空,那就遍历左右子树,求左右子树的最大值
        int leftHeight = maxDepth(root.left);
        int rightHeight = maxDepth(root.right);
        // 最后返回的就是左右子树高度的最大值+1
        return Math.max(leftHeight, rightHeight) + 1;
    }
}

二叉树的叶子节点数

二叉树的叶子节点数也可以基于递归的思想实现,上代码:

public class Solution {
    public int countOfLeaf(TreeNode root) {
        if (root == null) return 0;
        // 这里不管是||还是&&,都是可以实现的
        // 需要思考一下,为什么?
        // if (root.left == null && root.right == null) return 1;
        if (root.left == null || root.right == null) return 1;
        // 返回左右子树的叶子节点数的和
        return countOfLeaf(root.left) + countOfLeaf(root.right);
    }
}

总结:这里仅介绍了二叉树中最基础的算法,当然这也是你进阶二叉树其他算法的基础,请务必要理解掌握。同时实现上算法的方式很多,你可以有其他的方式实现,相信自己。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li解Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值