二叉树层序遍历

98 阅读4分钟

前言:

大家好,我是felix,前几天由于工作还有感冒,没有更新,今天我们来讨论一下二叉树的层序遍历,算是填补之前的坑了,因为之前我们已经说过了二叉树的其他遍历方式,详情可见之前的文章。

今天我们就说一下两种方式实现层序遍历,并且,还有进阶的Z型遍历方式。

什么是二叉树的层序遍历

如下图就是一个二叉树,而过是层序遍历的话,顺序应该为【5, 3, 6, 2, 4, 1, 7】

这个应该很好理解,就是像是楼房一样,一层一层,从左往右读下来。这个很符合我们的思维模式。

如何实现呢

第一种方法

思路:

试想一下,如果你去顺序遍历一个list, 你如何保证你遍历的结果是有序的呢?

    * 因为List本身就有索引,每个元素序号本来就有了,所以当然有序。

那么,二叉树我们能不能去编号呢,这样,我经过某个节点的时候,放他就放到List的某个序号的位置上,这样,等放完了,我直接按照顺序去遍历这个list不就行了嘛?

    * 其实,解法很快就要出来了,重点就是一个二叉树,如何编号满足层序遍历的要求。

其实我们可以这样去实现,根节点的序号为1,它的左节点就是2 * 1,右节点就是2 * 1 + 1,后面依次类推。如下图所示。

然后我们把他们按照序号放到一个List中,这样就可以实现啦。但是,要注意,list的size问题,看代码

/**
 * 
 * @param root 遍历到的节点
 * @param k 节点的序号
 * @param ans   用来保存结果的list
 */
public static void levelIterator(TreeNode root, int k, List<Integer> ans) {
    if (null == root) return;

    // 这里首要要获取当前list的大小
    //然后再索引从size 到k都要放上null,为了防止后面add的时候发生空指针
    int size = ans.size();
    for (int i = size;i <= k;i ++) {
        ans.add(i, null);
    }

    // 这里把当前节点的值放入对应的list的位置
    ans.add(k, root.val);
    // 判断左节点是否为空,若不为空,则进入迭代方法,变化的就是他的序号,应该是2 * k
    // 注意:这里对于k * 2 我用了位运算,左移两位,对于程序会比较友好一些
    if (null != root.left)
        levelIterator(root.left, k << 1, ans);

    // 对于右节点的遍历如上,但是要遍历我们的最终结果ans的时候,要去除掉null节点哦
    if (null != root.right)
        levelIterator(root.right, (k << 1) + 1, ans);

}

第二种方法

有时候面试官可能会刁难我们,然我们返回结果按照一层一层,隔开返回,并且每一层的遍历顺序交叉着来。

啥意思呢?还是上述的二叉树,我们要返回【【5】【6 3】【2 4 1 7】】,这种顺序就像是一条蛇在走Z字型,所以也叫Z型遍历二叉树。不过我认为S型应该更合理吧。

那这个应该如何实现呢?

思路:

- 首先,返回的结果是一层一层的,所以我们需要一个queue, 里面放的都是list
- 其次,每次的list顺序可能不一致,所以最好是LinkedList,因为这个list是双向的,我们可以操作
- 我们可以根据queue里面的size去决定我们应该正向添加linkedList,还是反向添加

好的,我们看下代码

/**
 *
 * @param root 二叉树的根节点
 * @return  返回的最终结果
 */
public static List<LinkedList> levelIteratorByZOrder(TreeNode root) {
if (null == root) return Collections.emptyList();

// 创建一个List去保存结果, 里面的泛型是LinkedList, 因为顺寻不一定
List<LinkedList> ans = new ArrayList<>();

// 再创建一个queue, 用来保存我们每层的二叉树节点
// 为啥用一个queue呢? 答案是复用,不管多少层,我们可以一直用它,可以看细品一下
Queue<TreeNode> queue = new LinkedList<>();

// 根节点放入queue中, 第一层就有了
queue.add(root);

// 开始遍历这个queue
while (!queue.isEmpty()) {
    // 创建一个LinkedList 用来存放每层遍历的结果,遍历一层创建一个
    LinkedList<Integer> tempLevel = new LinkedList<>();

    // 获取到这个queue的长度,因为我们从queue中弹出元素就依赖这个长度
    int queueSize = queue.size();
    for (int i = 0;i < queueSize;i ++) {
        TreeNode node = queue.poll();

        // 弹出来的元素保存到LinkedList中,但是顺序要注意
        // 这里很巧妙,使用ans 最终这个结果list的长度来判断, 细品一下
        if (ans.size() % 2 == 0)
            tempLevel.addLast(node.val);
        else
            tempLevel.addFirst(node.val);

        // 到这里, 也不要忘记把下一层节点放到queue里面
        if (node.left != null)
            queue.add(node.left);

        if (node.right != null)
            queue.add(node.right);
    }


}

return ans;
}

说实话: 这个代码,我是研究了很多遍才懂的,还是要多debug看一下,对于我们理解java中的一些数据结构非常有用,最后,很感谢给我点赞收藏关注的朋友们,本来是给自己学习用的,没想到帮到一些朋友,我很开心!

好了,这就是今天的算法分享啦!