Though tail-recursion optimization isn't required by C, if you can transform it into tail-recursion (and you can without a lot of work here), then, when that optimization is applied, you can maintain the readability, clarity, and conciseness of recursion with the same performance (time and space) as the best non-recursive solution.
I've slightly modified the function's conditions so it can work on an empty list with no nodes (where p is null) and will return null in that case. This is tail-recursive, and does require another parameter:
ListNode* findLargestRecurse(ListNode* p, ListNode* largest) {
// this is an implementation detail, and would not be called directly
// requires that largest is not null, but p may be null
if (!p) return largest;
if (p->item > largest->item) largest = p;
return findLargestRecurse(p->next, largest);
}
// preconditions: list head pointer is passed as a parameter, and may be null
// postcondition: returns the node with the largest value, or null
ListNode* findLargest(ListNode* p) {
if (!p) return 0; // mark A, see below
return findLargestRecurse(p->next, p);
}
Notice you use the main entry, findLargest, to setup the initial conditions/context for the actual recursion, findLargestRecurse.
However, I'd write that tail-recursion as an iterative while loop to rely less on what's currently both very compiler-specific and generally unfamiliar in C, which is not hard to do:
// preconditions: list head pointer is passed as a parameter, and may be null
// postcondition: returns the node with the largest value, or null
ListNode* findLargest(ListNode* p) {
ListNode* largest = 0;
for (; p; p = p->next) {
if (!largest || p->item > largest->item) {
largest = p;
}
}
return largest;
}
You can separate out the !largest
condition from the loop by doing it beforehand, by checking a base case just like the first findLargest does ("mark A").
And looking at this now, you may wonder why I called the recursive version more concise: it really isn't for this trivial example. That's partially because C is designed to favor iteration (notice the for loop in particular), somewhat because I tried to be verbose in the recursion instead of squashing it down as I would normally (so you could understand it easier), and the rest is because this is just a simple problem.