1) В коде ошибка - два последних if-а, с рекурсивными вызовами, должны быть за while (i <= j), а не в нем.
Этот while-цикл - совершает разбиение элементов так, что элементы в s..j - меньше pivot, а в i..f - больше.
Поддерживается инвариант, что слева от i - только элементы меньше-равные pivot, а справа от j - больше-равные.
Мы вообще игнорируем, где в массиве pivot, берем только его значение. Неважно в центре он, или вообще первый элемент в массиве.
Сначала в цикле двигаем i до упора, пропуская меньшие элементы. Обратите внимание - сравнение строгое. По инварианту, правее j - элементы большие-равные pivot, поэтому цикл остановится всегда, если было хоть раз сдвинуто на предыдущей итерации внешнего цикла while. В противном случае - это первая итерация - и где-то в массиве есть сам pivot элемент - на нем цикл точно остановится.
Потом делаем то же самое но с другой стороны, уменьшая j. Теперь у нас следующая ситуация: arr[s..i-1
]<=pivot, arr[i]>=pivot, ???, arr[j]<=pivot, arr[j+1..f]>=pivot. После помены i-того и j-того элементов местами, мы можем сдвинуть i и j на еще одну позицию - ведь теперь новый arr[i]<=pivot, что мы и должны поддерживать в инварианте.
2 крайних случая: если i==j, то, очевидно, arr[i]==arr[j]=pivot, и можно без нарушения инварианта сдвинуть указатели. Если i>j после вложенных while - это говорит о том, что arr[j..i] == pivot, arr[j-1]<=pivot, arr[i+1]>=pivot, и можно не сортировать часть массива между j и i.
Отвечая на ваш вопрос: Если слева от pivot недостаточно элементов, то i будет указывать на pivot - и при swap-е сдвинет pivot в правый конец, за j. После этого момента между i и j может вообще не быть элемента равного pivot. Но левее i и правее j Будут те элементы, на которых циклы точно остановятся.