java基础

二叉树算法—广度搜索算法使用以及变形

2021-07-13  本文已影响0人  小胖学编程

二叉树的广度搜索算法,不仅可以用来遍历二叉树,其算法亦可以变形使用解决其他二叉树问题。

1. 思索

使用迭代来实现广度搜索

  1. 需要什么数据结构来存储节点信息?
  2. 迭代的终止条件?

思索下面几个算法题:

102. 二叉树的层序遍历
429. N 叉树的层序遍历
107. 二叉树的层序遍历 II

101. 对称二叉树

2. 分析

广度搜索:即在水平维度一层层的去解析二叉树节点。即节点先进先出(队列结构)。

   public List<Integer> levelOrder(TreeNode root) {
        if (root == null) {
            return null;
        }

        List<Integer> results = new ArrayList<>();
        //使用队列来存储
        Queue<TreeNode> queue = new ArrayQueue<>();
        //根节点入栈
        queue.add(root);
        while (!queue.isEmpty()) {
            //出栈
            TreeNode node = queue.poll();
            //存储节点的值
            results.add(node.val);

            //左右节点存入
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        return results;
    }

最简单的算法如上所示。

3. 变形

3.1 层序遍历

image.png

看到这个题,就想到了广度搜索算法。

难点:如何将每一层的节点均存储到一个集合中?

开始想到的思路是:每一层末尾节点后加一个标识位表示分层?但是这种思路是不太好的!

那如何实现分层存储?

思路:首先广度搜索需要有一个大循环来遍历树,在大循环内部可不可以使用内层循环来遍历层呢?即大循环开启的是每层的查询,小循环开启的是每层节点的查询?

难点:如何控制内循环的循环次数?

思路:每遍历一个节点,会将节点所以子节点存储到队列中。当遍历完一次,结束内循环时,队列里面存储的均是下一层的节点,而队列的大小就是内循环的次数。

  /**
     * 广度遍历。
     * 大集合套小集合,思路是必须使用两个循环。
     * 大循环控制整棵树的遍历次数;
     * 小循环控制每一层的遍历次数;
     * <p>
     * 思路:因为结果集是List<List<Integer>>格式,那么一层循环是肯定无法设置的,需要两个循环
     * 1. 外层循环控制树的遍历;
     * 2. 内存循环控制层级的遍历;
     * <p>
     * 外层循环,queue数量为1;
     * 内存循环,将root的left和right存入后,
     * 外层循环,queue数量为2;
     * 内存循环,解析的数量为2节点,在内存循环中将个数全部循环完毕,且存入新的集合中。
     * 外层循环...
     */
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> results = new ArrayList<>();
        if (root == null) {
            return results;
        }
        //使用队列
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            //层级的节点数量
            int size = queue.size();
            //每一层的遍历结果
            List<Integer> result = new ArrayList<>();
            while (size > 0) {
                //数据的出栈
                TreeNode node = queue.poll();

                //两个节点填充
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
                result.add(node.val);
                //控制内层循环结束
                size--;
            }
            results.add(result);
        }
        return results;
    }

107. 二叉树的层序遍历 II这个提醒,就是在组装results时候,使用的是add(0,result)方法,将结果放在集合的第一个元素。

2.2 对称二叉树

image.png

这个题如何使用广度搜索来实现?

说实话我是没有思路的,可能是广度遍历二叉树对我影响太深,以至于广度搜索和核心我到现在都没明白。

这个题的难点在于:如何比较最左和最右两个节点是否相等。

广度搜索,使用队列结构来实现,节点进入队列后,然后取出做相应的操作,最后将其所有的子节点再次放入队列,实现对整棵树的遍历。

而本题的思想是两个节点的比较,那么在循环开始前,将root的左右节点放入到队列中,开启循环后,在队列中取出两个节点进行比较,若两个节点相等,按照顺序,将最左和最右两个节点在次放入到队列中。

每次取出来的两个节点是有序的(最左和最右)

    /**
     * 解答成功:
     * 执行耗时:1 ms,击败了28.54% 的Java用户
     * 内存消耗:38 MB,击败了5.11% 的Java用户
     * 使用广度
     */
    public boolean isSymmetric(TreeNode root) {

        if (root == null) {
            return true;
        }

        //每次比较左右两个节点
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root.left);
        queue.add(root.right);
        while (!queue.isEmpty()) {
            //取出节点
            TreeNode leftNode = queue.poll();
            TreeNode rightNode = queue.poll();
            if (leftNode == null && rightNode == null) {
                continue;
            }
            if (leftNode != null && rightNode == null) {
                return false;
            }
            if (leftNode == null && rightNode != null) {
                return false;
            }
            if (leftNode != null && rightNode != null) {
                if (leftNode.val != rightNode.val) {
                    return false;
                }
            }
            //子节点按照顺序两两组合,放入队列中
            queue.add(leftNode.left);
            queue.add(rightNode.right);
            queue.add(leftNode.right);
            queue.add(rightNode.left);
        }
        //循环结束还成功,那么就是真正匹配
        return true;
    }
上一篇 下一篇

猜你喜欢

热点阅读