Вот краткое объяснение.
Рассмотрим множество X = {x1, x2, ..., xn}. Перестановка X должна начинаться с некоторого xi, за которым следует перестановка X \ {xi}.
Хитрая реализация C делает именно это, используя следующий инвариант: каждый вызов permute () возвращает, оставляя массив неизменным (по сути, он вычисляет перестановку массива, распечатывает его, затем отменяет перестановку). Вот что делают эти строки:
// Permute a[i..n]:
swap((a+i), (a+j)); // Make a[j] the start of this (sub-)permutation starting at i.
permute(a, i+1, n); // Find the permuations of a[i+1..n] - and undo them.
swap((a+i), (a+j)); // Undo the swap of a[i] and a[j].