Skip to content
Ayotl

← Data structures from scratch · Lesson 19/32

Level-order traversal

✕
1/8

Floor by floor, with a queue

Depth-first goes to the bottom of a branch. Level-order traversal (BFS, breadth-first search) does the opposite: the root first, then all its children, then all the grandchildren.

It isn't done with recursion but with a queue: put the root in; while the queue isn't empty, take the one at the front, visit it and line its children up at the back. Since a queue is first in, first out, everyone on one floor always comes out before anyone on the next.

3920157
leccion.js

$ node leccion.js

Press RUN (or Ctrl+Enter).

What to look at

The order is 3, 9, 20, 15, 7: floor by floor, left to right. Look at the queue after visiting 20: its children, 15 and 7, lined up at the back and come out in the next two rounds.

The whole lesson on one page

Floor by floor, with a queue

Depth-first goes to the bottom of a branch. Level-order traversal (BFS, breadth-first search) does the opposite: the root first, then all its children, then all the grandchildren.

It isn't done with recursion but with a queue: put the root in; while the queue isn't empty, take the one at the front, visit it and line its children up at the back. Since a queue is first in, first out, everyone on one floor always comes out before anyone on the next.

3920157
class NodoArbol {
  constructor(valor, izq = null, der = null) {
    this.valor = valor;
    this.izq = izq;
    this.der = der;
  }
}

function arbol(niveles) {
  if (!niveles.length || niveles[0] === null) return null;
  const raiz = new NodoArbol(niveles[0]);
  const cola = [raiz];
  let i = 1;
  while (i < niveles.length) {
    const nodo = cola.shift();
    if (niveles[i] != null) { nodo.izq = new NodoArbol(niveles[i]); cola.push(nodo.izq); }
    i++;
    if (i < niveles.length && niveles[i] != null) { nodo.der = new NodoArbol(niveles[i]); cola.push(nodo.der); }
    i++;
  }
  return raiz;
}

function porAnchura(raiz) {
  if (raiz === null) return;
  const cola = [raiz];
  while (cola.length > 0) {
    const nodo = cola.shift();                    // the front one leaves
    if (nodo.izq) cola.push(nodo.izq);            // its children go to the back
    if (nodo.der) cola.push(nodo.der);
    console.log("visit", nodo.valor, "| queue now:", cola.map((n) => n.valor));
  }
}

porAnchura(arbol([3, 9, 20, null, null, 15, 7]));

The order is 3, 9, 20, 15, 7: floor by floor, left to right. Look at the queue after visiting 20: its children, 15 and 7, lined up at the back and come out in the next two rounds.

Each level in its own array

Sometimes the order isn't enough: you want to know where each floor ends. The trick is to check how many are in the queue when a round starts: those are exactly the current level. Take out those tam, collect them in an array, and the children queued meanwhile are the next level.

That gives [[3], [9, 20], [15, 7]], which is the "Binary Tree Level Order Traversal" challenge.

class NodoArbol {
  constructor(valor, izq = null, der = null) {
    this.valor = valor;
    this.izq = izq;
    this.der = der;
  }
}

function arbol(niveles) {
  if (!niveles.length || niveles[0] === null) return null;
  const raiz = new NodoArbol(niveles[0]);
  const cola = [raiz];
  let i = 1;
  while (i < niveles.length) {
    const nodo = cola.shift();
    if (niveles[i] != null) { nodo.izq = new NodoArbol(niveles[i]); cola.push(nodo.izq); }
    i++;
    if (i < niveles.length && niveles[i] != null) { nodo.der = new NodoArbol(niveles[i]); cola.push(nodo.der); }
    i++;
  }
  return raiz;
}

function niveles(raiz) {
  const r = [];
  if (raiz === null) return r;
  const cola = [raiz];
  while (cola.length > 0) {
    const tam = cola.length;        // the ones on this level
    const nivel = [];
    for (let i = 0; i < tam; i++) {
      const nodo = cola.shift();
      nivel.push(nodo.valor);
      if (nodo.izq) cola.push(nodo.izq);
      if (nodo.der) cola.push(nodo.der);
    }
    r.push(nivel);
  }
  return r;
}

console.log(niveles(arbol([3, 9, 20, null, null, 15, 7])));
console.log(niveles(arbol([8, 3, 10, 1, 6, null, 14])));

One array per floor. The number of arrays is the tree's height, and the longest one tells you the widest floor.

What you see from the right

With the levels separated, problems that would be messy depth-first come out easily. If you stand to the right of the tree, on each floor you only see the last node: the "right side view" is the last of each level.

A note on cost: shift() on an array is O(n), because it moves everything to close the gap (you saw this in the queues unit). For small trees it doesn't matter; for millions of nodes, use the queue with a front index.

class NodoArbol {
  constructor(valor, izq = null, der = null) {
    this.valor = valor;
    this.izq = izq;
    this.der = der;
  }
}

function arbol(niveles) {
  if (!niveles.length || niveles[0] === null) return null;
  const raiz = new NodoArbol(niveles[0]);
  const cola = [raiz];
  let i = 1;
  while (i < niveles.length) {
    const nodo = cola.shift();
    if (niveles[i] != null) { nodo.izq = new NodoArbol(niveles[i]); cola.push(nodo.izq); }
    i++;
    if (i < niveles.length && niveles[i] != null) { nodo.der = new NodoArbol(niveles[i]); cola.push(nodo.der); }
    i++;
  }
  return raiz;
}

function vistaDerecha(raiz) {
  const r = [];
  const cola = raiz ? [raiz] : [];
  let frente = 0;                   // instead of shift(): an index
  while (frente < cola.length) {
    const fin = cola.length;        // the level runs from frente to fin
    for (let i = frente; i < fin; i++) {
      const nodo = cola[i];
      if (i === fin - 1) r.push(nodo.valor);   // last one on the floor
      if (nodo.izq) cola.push(nodo.izq);
      if (nodo.der) cola.push(nodo.der);
    }
    frente = fin;
  }
  return r;
}

console.log(vistaDerecha(arbol([1, 2, 3, null, 5, null, 4])));
console.log(vistaDerecha(arbol([1, 2, 3, 4])));
  • First tree: 1, 3 and 4, the ones on the right edge.
  • Second: 1, 3 and 4, even though 4 hangs on the left side: on its floor there's nobody further right to hide it.

Practice with a video challenge

Further reading