getNodesToRestructureAfterRemove(traveledNodes) {
    // z is last traveled node - imbalance found at z
    const zIndex = traveledNodes.length - 1;
    const z = traveledNodes[zIndex];

    // y should be child of z with larger height
    // (cannot be ancestor of removed node)
    let y;
    if (z.left !== null && z.right !== null) {
      y = z.left === y ? z.right : z.left;
    } else if (z.left !== null && z.right === null) {
      y = z.left;
    } else if (z.right !== null && z.left === null) {
      y = z.right;
    }

    // x should be tallest child of y
    // If children same height, x should be child of y
    // that has same orientation as z to y
    let x;
    if (y.left !== null && y.right !== null) {
      if (y.left.height > y.right.height) {
        x = y.left;
      } else if (y.left.height < y.right.height) {
        x = y.right;
      } else if (y.left.height === y.right.height) {
        x = z.left === y ? y.left : y.right;
      }
    } else if (y.left !== null && y.right === null) {
      x = y.left;
    } else if (y.right !== null && y.left === null) {
      x = y.right;
    }
    return [x, y, z];
  }
When a removal happens, some nodes need to be
restructured. Gets and return these nodes.