I would take the code just a little further and remove the multiple exit points. This allows you to reason about both the effect on the list and which node mut be returned.
Node appendRecursive(Node head, int data) {
// By default return the same list we were given.
Node list = head;
if (list == null) {
// At end of list or list is empty.
list = new Node();
list.data = data;
} else {
// Recurse.
head.next = appendRecursive(head.next, data);
}
return list;
}
As far as reasoning goes you generally need to use induction.
- If the list is empty (
list == null
) then create a new node and that becomes your list.
- If the list is not empty, your new list must be the list with the new data appended to it.
Given the above it is possible to reason that in all cases this will function correctly because either the list is empty or it is not.
Using recursion on lists is generally considered inefficient and clunky because iterative algorithms fit better with linear structures. A better exercise would be to write your own Tree
structure because trees lend themselves very well to recursive algorithms. You will discover that it is often much easier and more elegant to perform the required function recursively on a tree.
static class Tree {
Node head = null;
class Node {
Node left;
Node right;
int data;
private Node(int data) {
this.data = data;
}
}
void insert(int data) {
head = insert(head, data);
}
private Node insert(Node node, int data) {
if (node == null) {
// Empty tree becomes just the node.
return new Node(data);
} else {
// Pick the correct branch to add this data to.
if (data < node.data) {
node.left = insert(node.left, data);
} else {
node.right = insert(node.right, data);
}
}
return node;
}
private CharSequence toString(Node n) {
StringBuilder s = new StringBuilder();
if (n != null) {
// First print the tree on the left.
if (n.left != null) {
s.append(toString(n.left)).append(",");
}
// Then the data in this node.
s.append(n.data);
// Then the tree on the right.
if (n.right != null) {
s.append(",").append(toString(n.right));
}
}
return s;
}
@Override
public String toString() {
// Even toString is recursive.
StringBuilder s = new StringBuilder("{");
s.append(toString(head));
return s.append("}").toString();
}
}
public void test() {
Tree tree = new Tree();
for (int i : new int[]{6, 5, 4, 3, 2, 1}) {
tree.insert(i);
}
System.out.println(tree);
}
Notice how simple it is to judge where to add "," in the toString
method - a notoriously clunky issue when printing lists.