Прежде всего, нам нужно исправить check_missing_a
, потому что он глючит. После обмена a[i]
может выйти за пределы для следующего a[a[i]]
. Исправленная версия:
int check_missing_a2(int *a, int n) {
for (int i = 0; i < n; ++i) {
while (i != a[i]) {
if (a[i] < i || a[i] >= n || a[i] == a[a[i]])
return 0;
swap(&a[i], &a[a[i]]);
}
}
return 1;
}
Мы даже можем сохранить несколько сравнений следующим образом: (Спасибо @chmike)
int check_missing_a2(int *a, int n)
{
for (int i = 0; i < n; ++i)
if (a[i] < 0 || a[i] >= n)
return 0;
for (int i = 0; i < n; ++i) {
while (i != a[i]) {
if (a[i] == a[a[i]])
return 0;
swap(&a[i], &a[a[i]]);
}
}
return 1;
}
Сложность check_missing_a2
На первый взгляд можно подумать, что check_missing_a2
медленнее, чем O (N), потому что внешний l oop делает N проходов, и есть еще один внутренний l oop.
Однако внутренний l oop выполняет не более N-1 перестановок. Например, следующее иллюстрирует количество свопов для каждого расположения чисел в 0..N-1 для N = 8:
# swaps # arrangements
------- --------------
0 1
1 28
2 322
3 1960
4 6769
5 13132
6 13068
7 5040
Как объясняется @ 4386427, каждый своп размещает по крайней мере один элемент в его правильная позиция. Следовательно, не может быть больше N перестановок.
Это означает, что ни одна часть функции не будет выполнена более 2 * N раз, что приведет к сложности O (N).
Сложность check_missing_b
Один l oop с N проходами для сложности O (N).
Что касается фактическая производительность, я подозреваю, что check_missing_a2
всегда будет быстрее, чем check_missing_b
.
Конечно, есть также проблема, что check_missing_a2
меняет массив и что check_missing_b
может переполниться.