前言:
大家好,我是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中的一些数据结构非常有用,最后,很感谢给我点赞收藏关注的朋友们,本来是给自己学习用的,没想到帮到一些朋友,我很开心!
好了,这就是今天的算法分享啦!