Meh, you've had a day to work it out, so here we go!
Here's one way of doing it O(n). First off, the starting predicate, (slne/2
). Given we're after the 2nd largest element, we'll assume you have an input list (of numbers) with length of at least two. Kick things off by comparing the relative values of the first two elements and record the current maximum and the current '2nd largest' (as suggested earlier, by Priyank), as arguments to a call to another predicate to do the work (slne/4
):
slne([E0, E1|Es], Res) :-
E0 > E1, !,
slne(Es, E0, E1, Res).
slne([E0, E1|Es], Res) :-
slne(Es, E1, E0, Res).
Secondly, now that we've got a starting point of reference, recurse through the remainder of the list (if any) and return the second largest element (SecMax), in (slne/4
).
The base case: No more elements left! We're finished.
slne([], _, Res, Res).
The next case: we find a new local maximum at the start of the list:
slne([E|Es], Max, _SecMax, Res) :-
E >= Max, !,
slne(Es, E, Max, Res).
(Note that we threw away the current second largest (SecMax), because we assume that it was smaller than or equal to Max).
The next case: we don't find a new local maximum, but we do find a new '2nd best':
slne([E|Es], Max, SecMax, Res) :-
E >= SecMax, !,
slne(Es, Max, E, Res).
The last case: Throw away other values, as they're irrelevant - look at the rest instead:
slne([_|Es], Max, SecMax, Res) :-
slne(Es, Max, SecMax, Res).
That's it. Personally, I like Juanjo's solution, mainly because it's 1 line of code, and the performance difference between O(n log n) and O(n) might be negligible in practice even for large input. (Also note that dsort/2
doesn't appear in all PROLOG implementations - e.g., in SWI-PL, you might try msort/2
instead).