Here's a bit of a variant on the other answers, which provides on average from 3.33% to 66.67% improvement over 6 comparisons, and fully partitions the 5 elements around their median as a bonus at no extra cost.
It's possible to find the median of 5 distinct elements in an average of 5.8 comparisons (averaged over all permutations) by using median-of-3 and quickselect, using median-of-3 to select the pivot from a sample of 3 of the 5 elements. Median-of-3 partitions those three elements, which need not be recompared to the pivot when partitioning the remaining 2 elements. So far that's 4-5 comparisons to partition the 5 elements around one of the three middle elements (the median-of-3 cannot be either the minimum or maximum of 5). Up to 3 more comparisons may be necessary to partition the 5 elements around their median (which strictly speaking is more work than merely finding the median), for a total ranging from 4 to 7 comparisons, with (as mentioned) an average of 5.8 over all possible permutations of 5 distinct elements (fewer comparisons if the elements are not distinct). Note that this differs from the usual always-6-comparisons solutions, in that a few cases of distinct inputs may require as many as 7 comparisons, but on the other hand, most permutations of distinct inputs require no more than 6, and often fewer; moreover it is fairly easy to code to save comparisons for non-distinct inputs (only 2 comparisons are required if all inputs are equal; the code to save comparisons when inputs are not distinct using the usual 6-comparison method becomes rather convoluted (try it!), and without that it still takes 6 comparisons even when all inputs are equal).
Order statistics other than the median can be found this way: the 2nd smallest or 2nd largest can be found in on average slightly more (5.81666... comparisons), and of course it's possible to find the minimum or maximum with only 4 comparisons.
Based on that, and by request, here's a heavily commented function to return a pointer to the median of 5 elements, using a variadic comparison function. It's written in C, but it should work just as well in the quadrathorpe-y deviant or in sea ploose ploose. Note that this returns a pointer to the median-valued element only; it does not partition the elements (in fact it doesn't move any elements).
/* a virtual swap, preserving both pointers for later use */
#define VSWAP(ma,mb,mt) mt=ma,ma=mb,mb=mt
/* a virtual swap only preserving the first pointer */
#define VSWAP2(ma,mb,munused) ma=mb
/* virtual rotation to the left; reverse the first 3 args for right rotation */
#define ROTATE(ma,mb,mc,mt) (mt)=(ma),(ma)=(mb),(mb)=(mc),(mc)=(mt)
/* median of 5 elements, no data movement, minimum (average 5.8) comparisons */
/* This implementation minimizes the number of comparisons made (min. 2) when
elements compare equal.
*/
/* As no elements are moved, the elements are of course not partitioned around
the element pointed to by the returned pointer (this differs from selection
of the median via quickselect).
*/
/* Results are biased toward the middle: pc, then pb or pd, last pa or pe. */
/* The implementation is based on a simulation of quickselect using partition3
to select a pivot from the middle three elements, with partitioning by
skipping over the 3 partitioned elements. For distinct inputs, it uses on
average 5.8 comparisons (averaged over all permutations of 5 distinct
elements); fewer for inputs with equal elements. That's an improvement of
about 3% over the usual 6-comparison implementation of median-of-5.
*/
void *fmed5(void *pa, void *pb, void *pc, void *pd, void *pe,
int(*compar)(const void *,const void *))
{
void *t;
int c, d;
/* First compare the 3 middle elements to determine their relative
ordering; pb will point to the minimum, pc to the median, and pd to
the maximum of the three.
*/
/* Ternary median-of-3, biased toward middle element if possible, else
first element. Average 8/3 comparisons for 3 elements (distinct values)
= 0.889 comparisons/element
*/
c=compar(pb,pc); /* 1 */
if (0<c) { /* *pb,*pc out-of-order: *pb>*pc */
/* Before doing anything about pb,pc, compare *pc,*pd. */
d=compar(pc,pd); /* 2a */
if (0>d) { /* *pc<*pd: strictly in order */
/* But *pb might be either larger than or smaller than (or equal
to) *pd, so they may (i.e. unless it's known from the earlier
comparison of original *pc and *pd that *pb is larger than
both) have to be compared, Possibilities:
*pc<*pb<=*pd (virtual swap of pb,pc corrects relationship)
*pc<*pd<*pb (virtual rotation to the left corrects it)
*/
c=compar(pb,pd); /* 3a (if needed) */
if (0<c) { /* *pc < *pd < *pb */
ROTATE(pb,pc,pd,t);
} else { /* *pc < *pb <= *pd */
VSWAP(pb,pc,t);
}
} else { /* *pd==*pc<*pb or reversed ordering: *pd<*pc<*pb */
VSWAP(pb,pd,t); /* one (virtual) swap takes care of it */
} /* *pc:*pd comparisons results if-else */
/* Note that if pc,pd compare equal, pc remains as the chosen
median (biased toward the middle element).
*/
} else if (0==c) { /* *pb,*pc compare equal */
/* Either pb or pc can be taken as the median; bias here is towards
pc, which is already in the middle position. But pd might be
the minimum of the three or the maximum (or it may also be equal
to both pb and pc).
*/
d=compar(pb,pd); /* 2b */
if (0<d) { /* pb,pd are out-of-order */
VSWAP(pb,pd,t);
}
} else { /* *pb,*pc in strict order: *pb < *pc; how about *pc,*pd? */
d=compar(pc,pd); /* 2c */
if (0<d) { /* *pc,*pd are strictly out-of-order: *pd < *pc */
/* But *pb might be either larger than or smaller than (or equal
to) *pd, so they may (i.e. unless it's known from the earlier
comparison of original *pc and *pd that *pb is larger than
both) have to be compared, Possibilities:
*pb<=*pd<*pc (virtual swap of pc,pd corrects relationship)
*pd<*pb<*pc (virtual rotation to the right corrects it)
*/
c=compar(pb,pd); /* 3b (if needed) */
if (0<c) { /* *pd < *pb < *pc */
ROTATE(pd,pc,pb,t);
} else { /* *pc < *pb <= *pd */
VSWAP(pc,pd,t);
}
} /* *pc:*pd comparisons results if-else */
/* Note that if pc,pd compare equal, pc remains as the chosen
median (biased toward the middle element).
*/
} /* *pb:*pc comparisons results if-else chain */
/* Now pb points to the minimum of pb,pc,pd; pc to the median, and pd
to the maximum.
*/
/* Special case: if all three middle elements compare equal (0==c==d),
any one can be returned as the median of 5, as it's impossible for
either of the other two elements to be the median (unless of course
one or both of them also compares equal to pb,pc,pd, in which case
returning any of pb,pc,pd is still correct). Nothing more needs to
be done in that case.
*/
if ((0!=c)||(0!=d)) { /* Not all three of pb,pc,pd compare equal */
int e;
/* Compare pa and pe to pc. */
e=compar(pa,pc); /* 3c or 4a (iff needed) */
/* If three (or more) of the four elements so far compared are
equal, any of those equal-comparing elements can be chhosen as
the median of 5. If all five elements were arranged in order,
one of the three equal-comparing elements would necessarily be
in the middle (at most both of the remaining elements might be
either larger or smaller than the equal elements). So if pa
compares equal to pc and pc also compared equal to pb or to pd,
nothing more need be done; pc can be considered as the median of
five.
*/
if ((0!=e)||(0!=c)||(0!=d)) { /* no three elements compare equal */
int f;
f=compar(pe,pc); /* 4b or 5a (iff needed) */
/* Check again for three equal comparisons to avoid doing any
unnecessary additional work.
*/
if (
(0!=f) /* also not equal; still not three */
||
( /* 0==f && */
((0!=c)&&(0!=d)) /* at most e and f; not three */
||
((0!=c)&&(0!=e)) /* at most d and f; not three */
||
((0!=d)&&(0!=e)) /* at most c and f; not three */
)
) {
/* Possibilites are that:
one of *pa,*pe is less than (or equal to) *pc and one
is greater than (or equal to) *pc; *pc is the median
of five.
*pa and *pe are both less than *pc; the median of five
is then the maximum of *pa,*pb,*pe (*pc and *pd are
at least as large as those three). The ordering of
those 3 elements has not been established, and it
will take two additional comparisons to do so.
*pa and *pe are both greater than *pc; the median of
five is the minimum of *pa,*pd,*pe (neither *pb nor
*pc can be larger than any of those three).
*/
if ((0<e)&&(0<f)) { /* *pa,*pe both > *pc; median of five is
the minimum of *pa,*pe,*pd
*/
/* Bias towards *pd (originally closest of these three
to the middle. Neither *pa nor *pe has yet been
compared to *pd.
*/
e=compar(pa,pe); /* 5b or 6a (iff needed) */
if (0<e) { /* *pe is smaller */
f=compar(pd,pe); /* 6b or 7a (iff needed) */
if (0<f) { /* *pe is smaller */
VSWAP2(pc,pe,t);
} else { /* *pd is smaller or *pd==*pe */
VSWAP2(pc,pd,t);
}
} else { /* *pa==*pe or *pa is smaller */
f=compar(pd,pa); /* 6c or 7b (iff needed) */
if (0<f) { /* *pa is smaller */
VSWAP2(pc,pa,t);
} else { /* *pd is smaller or *pd==*pa */
VSWAP2(pc,pd,t);
}
}
} else if ((0>e)&&(0>f)) { /* *pa,*pe both < *pc; median of
five is the maximum of *pa,*pb,*pe
*/
/* Bias towards *pb (originally closest of these three
to the middle. Neither *pa nor *pe has yet been
compared to *pb.
*/
e=compar(pa,pe); /* 5c or 6d (iff needed) */
if (0<e) { /* *pa is larger */
f=compar(pa,pb); /* 6e or 7c (iff needed) */
if (0<f) { /* *pa is larger */
VSWAP2(pc,pa,t);
} else { /* *pb is larger or *pa==*pb */
VSWAP2(pc,pb,t);
}
} else { /* *pe is larger or *pa==*pe */
f=compar(pe,pb); /* 6f or 7d (iff needed) */
if (0<f) { /* *pe is larger */
VSWAP2(pc,pe,t);
} else { /* *pb is larger or *pe==*pb */
VSWAP2(pc,pb,t);
}
} /* *pa:*pe results if-else */
} /* median of five: minimum or maximum of three if-else */
} /* not three equal elements among five */
} /* not three equal elements among four */
} /* not three equal elements among three */
return pc;
}