Binary Trees · 13 of 35
Zig-Zag traversal of a binary tree
Read a tree row by row like a snake: left to right on one row, right to left on the next, and keep switching.
- Simple way
- time O(n)
room O(w) - Clever way
- time O(n)
room O(w)
The problem, simply
You get a binary tree. Read it one row (level) at a time, but change direction on every row: the top row left to right, the next row right to left, the next left to right again, and so on. Give back each row as its own list.
A story to remember it
Picture it
The snake path: row 0 is 1, row 1 is read 3 then 2, row 2 is read 4, 5, 6, 7, and row 3 is read 9 then 8.
Examples
Four rows
- In
[1, 2, 3, 4, 5, 6, 7, null, null, 8, 9]- Out
[[1], [3, 2], [4, 5, 6, 7], [9, 8]]
Rows 1 and 3 are read right to left.
An uneven tree
- In
[3, 9, 20, null, null, 15, 7]- Out
[[3], [20, 9], [15, 7]]
Row 2 only has the children of 20, and it is read left to right again.
One node
- In
[1]- Out
[[1]]
One row, read left to right.
No tree
- In
[]- Out
[]
Return an empty list.
Way 1: the simple way
Do a normal row by row reading with a waiting line (a queue). Every second row, turn the row's list around before you save it.
- Put the root in the line. Set
leftToRight = true. - Read the line's size and take that many nodes, writing their values into a row list and adding their children to the back.
- If
leftToRightis false, reverse the row list. - Save the row, flip
leftToRight, and repeat.
Row 1 is read from the line as 2, 3 and then turned around to 3, 2 before it is saved.
- looking now
- done
- just added
import java.util.*;
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> out = new ArrayList<>();
if (root == null) return out;
Queue<TreeNode> line = new ArrayDeque<>();
line.add(root);
boolean leftToRight = true;
while (!line.isEmpty()) {
int size = line.size();
List<Integer> row = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode node = line.poll();
row.add(node.val);
if (node.left != null) line.add(node.left);
if (node.right != null) line.add(node.right);
}
if (!leftToRight) Collections.reverse(row); // turn the row around
out.add(row);
leftToRight = !leftToRight;
}
return out;
}
}How long it takes: each node once, plus turning some rows around, which also touches each node at most once, O(n). Extra room: one row in the line, O(w) where w is the widest row.
Way 2: the clever way
Use two piles (stacks): now holds the row you are reading, next collects the row below. You always pop from the top of now.
- Push the root on
now. SetleftToRight = true. - Pop a node from
nowand write it in the row. - If
leftToRight, push its left child then its right child onnext. Otherwise push right then left. - When
nowis empty, the row is finished. Save it, swap the piles, and flip the direction.
Why it works: the pile turns things around. Children pushed left then right come out right then left, which is exactly the next row's direction.
Two piles turn every row around for free
01 / 10
Put the root on the this row stack. Row 0 is read left to right.
- direction
- left to right
- looking now
- done
- answer
- path
- just added
import java.util.*;
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> out = new ArrayList<>();
if (root == null) return out;
Deque<TreeNode> now = new ArrayDeque<>();
Deque<TreeNode> next = new ArrayDeque<>();
now.push(root);
boolean leftToRight = true;
List<Integer> row = new ArrayList<>();
while (!now.isEmpty()) {
TreeNode node = now.pop();
row.add(node.val);
if (leftToRight) { // next row comes out right to left
if (node.left != null) next.push(node.left);
if (node.right != null) next.push(node.right);
} else { // next row comes out left to right
if (node.right != null) next.push(node.right);
if (node.left != null) next.push(node.left);
}
if (now.isEmpty()) { // this row is finished
out.add(row);
row = new ArrayList<>();
Deque<TreeNode> swap = now;
now = next;
next = swap;
leftToRight = !leftToRight;
}
}
return out;
}
}How long it takes: every node is pushed and popped once, O(n), and nothing is ever reversed. Extra room: the two piles together hold at most two rows, O(w).
Watch out for
- Do not reverse the waiting line itself in Way 1. Only reverse the row list you save; the line must stay in normal left to right order.
- Using one stack instead of two mixes the current row with the next one.
- An empty tree returns an empty list.
What interviewers ask next
- Can you avoid the reverse in the queue version? Yes. Fill each row with a deque: add to the back on left to right rows and to the front on right to left rows.
- Print the whole zigzag as one flat list instead of rows. Same code, but add every value to one list.
- Spiral order of a matrix is a different problem; do not mix them up, but the "turn around every pass" feeling is the same.
- Which is better? All three are
O(n). The deque row version is the shortest to write in an interview; the two stack version shows you understand why stacks reverse.
Remember this
Grown-up words: rows are levels, the waiting line is a queue for breadth first search, the piles are stacks (last in, first out), and a line that you can use from both ends is a deque.