Ежедневный бит (е) C ++ # 9, общий вопрос интервью: наименьшее пропущенное положительное целое число

Сегодня мы рассмотрим распространенный вопрос на собеседовании — «Наименьшее пропущенное натуральное число».

Учитывая список целых чисел, определите наименьшее пропущенное положительное целое число. Важно отметить, что ваше решение должно работать за O(n) раз, и хотя вам разрешено изменять ввод, вы можете использовать только постоянную дополнительную память.

Например, для ввода {3, -1, 0, 4, 1} наименьшее отсутствующее положительное число равно 2, для ввода {1, 2, 3} наименьшее отсутствующее положительное число равно 4.

Прежде чем продолжить чтение решения, попробуйте решить его самостоятельно. Вот ссылка на Compiler Explorer с парой тестов: https://compiler-explorer.com/z/G7r4rebhd.

Решение

Тривиальным решением будет сортировка нашего ввода. Однако это будет O(n*logn) временной сложности.

Хотя это не совсем неправильное направление для исследования.

Важно отметить, что наше пропущенное число должно быть в диапазоне [1, N+1], и оно будет N+1 только в том случае, если входные данные содержат все числа в диапазоне [1, N]. Мы также не заботимся о дубликаты.

Эти две точки позволяют нам сформулировать более простую квази-сортировку, потому что мы знаем индекс назначения каждого из чисел в диапазоне [1, N]; это диапазон индексов [0, N-1].

Мы можем за один проход перебрать наш ввод, помещая каждое число в нужное место.

Затем, чтобы определить, какое число отсутствует, мы снова перебираем входные данные и возвращаем отсутствующее число или N+1, если мы закончим перебор входных данных, не найдя ни одного.

int lowest_missing(std::vector<int> data) {
  // O(n) "sort"
  for (auto idx : std::views::iota(0z, std::ssize(data))) {
    while (data[idx] > 0 && // if a positive number
           data[idx] - 1 < std::ssize(data) && // and does fit
           data[idx] != data[data[idx] - 1] && // this is not noop
           data[idx] - 1 != idx) // and not in correct place
      std::swap(data[idx], data[data[idx] - 1]); // move in correct place
  }
  // O(n) because each number is swapped at most twice
  // - first pulled to the current index
  // - then put into final place

  // O(n) search
  for (auto idx : std::views::iota(0z, std::ssize(data)))
    if (data[idx] - 1 != idx)
      return idx + 1;
  return std::ssize(data)+1;
}

Откройте решение в Compiler Explorer.