Assigning neighbor in a binary tree
Make Node generic

The problem description uses node values like A, B, C, ..., yet your Node class can only store int values. There's no need to choose, you can make Node generic:

class Node&lt;T&gt; {
    Node&lt;T&gt; left, right;
    Node&lt;T&gt; neighbor;
    T data;

    Node(T d) {
        left = null;
        right = null;
        data = d;
        neighbor = null;
    }
}


Bug

This implementation doesn't work for the given example data:

        A
     /     \
    B       C
 /    \      \
D      E       F


The neighbor of E should be F, but this implementation will give null.
This is because the only place you set the neighbor is this code:


if (parent.left != null &amp;&amp; parent.right != null) {
    parent.left.neighbor = parent.right;
}



This only works for nodes in left position. It doesn't for nodes in right position that have a neighbor.

But you're not too far off. With a few simple changes to the main loop, this can work:

        Node&lt;T&gt; prev = null;
        for (Node&lt;T&gt; node : parents) {
            if (node.left != null) {
                current.add(node.left);
            }
            if (node.right != null) {
                current.add(node.right);
            }
            if (prev != null) {
                prev.neighbor = node;
            }
            prev = node;
        }


Initializing generic types

There are several issues here:


ArrayList&lt;Node&gt; current = new ArrayList&lt;BinaryTree.Node&gt;();



The type of left-hand size is ArrayList&lt;Node&gt; but the right-hand side is ArrayList&lt;BinaryTree.Node&gt;. This only compiles if Node and BinaryTree.Node are the same class. But if they are the same class, then it would be better to refer to them the same way consistently.

...except you don't actually need to specify the generic type of the right-hand side, you can use the diamond operator &lt;&gt; and let the compiler figure out the correct type:

ArrayList&lt;Node&gt; current = new ArrayList&lt;&gt;();


Lastly, it's recommended to declare variables with their interface type rather than the implementation type. So this will be better:

List&lt;Node&gt; current = new ArrayList&lt;&gt;();


Alternative implementation

The current implementation tries to avoid adding null values in the list. The consequence of that is multiple null checks:


Before adding the root
Before adding a left child
Before adding a right child


Instead of having 3 places to check for nulls, you could allow null values in the list, and move the check inside the loop, in one place. Rearranging the code to work that way, the code becomes:

    List&lt;Node&lt;T&gt;&gt; level = new ArrayList&lt;&gt;();
    level.add(root);

    do {
        List&lt;Node&lt;T&gt;&gt; prevLevel = level;
        level = new ArrayList&lt;&gt;();
        Node&lt;T&gt; prevNode = null;
        for (Node&lt;T&gt; current : prevLevel) {
            if (current == null) {
                continue;
            }
            level.add(current.left);
            level.add(current.right);
            if (prevNode != null) {
                prevNode.neighbor = current;
            }
            prevNode = current;
        }
    } while (!level.isEmpty());


I also renamed some variables to slightly more intuitive names.