Найти диапазон I

Учитывая отсортированный массив чисел, найдите первый диапазон чисел, который содержит цель. Диапазон содержит цель, если низкий ‹= целевой ‹= высокий. Если диапазон не существует, верните [-1, -1]

Пример:
Массив: [1, 2, 3, 4, 5, 6]
Цель: 4
Вывод: [3, 4]

Массив: [1, 2, 3, 4, 5, 6]
Цель: 0
Выход: [-1, -1]

Попробуйте, а затем вернитесь, если вы застряли или найдете решение.

Решение №1

Первое решение действительно прямолинейно, мы начинаем со второго элемента, и по мере прохождения массива мы проверяем, находится ли цель между предыдущим и текущим значением. Если в какой-то момент целевое значение находится между ними, мы возвращаем эти два числа. Однако, если число не найдено, мы возвращаем [-1, -1].

Код (С++)

pair<int, int> find_range(vector<int> arr, int target) {
   for(int i = 1; i < arr.size(); i++) {
      if(arr[i-1] <= target && arr[i] >= target)
         return {arr[i-1], arr[i]};
   }
   return {-1, -1};
}

Время: O(n)
Пространство: O(1)

Как видите, код прост и прямолинеен, а временная сложность не страшна, но мы можем сделать лучше.

Если вы получили вышеуказанное решение, вернитесь и перечитайте проблему и посмотрите, сможете ли вы улучшить свое решение. Как только вы это сделаете, вернитесь и посмотрите, как я это сделаю, и сравните ваше решение.

Решение №2

Если мы вернемся и перечитаем вопрос, мы найдем небольшой самородок, который поможет нам улучшить нашу временную сложность.

Дан отсортированный массив чисел

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

Код (С++)

pair<int, int> find_range(vector<int> arr, int target) {
  int low = 0, high = arr.size() - 1, mid;
    while(low <= high) {
      mid = (high + low) / 2;
      if(arr[mid] <= target && arr[mid + 1] >= target) {
        return {arr[mid], arr[mid+1]};
      } else if(arr[mid] < target) {
        low = mid + 1;
      } else {
        high = mid - 1;
      }
    }
  return {-1, -1};
}

Мы реализовали бинарный поиск, и это приводит к тому, что наше время достигает O(log n), но если мы вернемся и перечитаем вопрос, мы обнаружим, что не охватываем все случаи.

первый диапазон чисел, который содержит цель

Если мы запустим код, мы обнаружим, что он не покрывает его для следующего случая
arr = [0 1 1 1 1 1 1 1 1 1 1]
target = 1
expect = [0, 1]
факт = [1, 1]

Чтобы покрыть этот пограничный случай, нам нужно выполнить некоторые проверки, когда мы узнаем, что цель находится в пределах досягаемости. Если наш настоящий и следующий совпадают, то мы можем спокойно вернуться такими, какие мы есть. Если они совпадают и предыдущий и первый не совпадают возвращаем предыдущий и текущий. Если это не так, у нас высокий уровень будет средним — 1.

Код (С++)

if(arr[mid] != arr[mid + 1])
  return {arr[mid], arr[mid + 1]};
if(arr[mid - 1] != arr[mid] && arr[mid] == arr[mid + 1])
  return {arr[mid - 1], arr[mid]};
high = mid - 1;

Хорошо, это было не так уж плохо, чтобы пройти тест. Однако у нас все еще есть проблема в коде. Если мы получим массив максимального размера и будем искать значение больше последнего значения, произойдет переполнение. Чтобы решить эту проблему, мы делаем простое исправление: среднее = низкое + ((высокое — низкое) / 2). Сделав это, наше окончательное решение будет

Код (С++)

pair<int, int> find_range(vector<int> arr, int target) {
  int low = 0, high = arr.size() - 1, mid;
    while(low <= high) {
      mid = low + ((high - low) / 2);
      if(arr[mid] <= target && arr[mid + 1] >= target) {
        if(arr[mid] != arr[mid + 1])
          return {arr[mid], arr[mid + 1]};
        if(arr[mid - 1] != arr[mid] && arr[mid] == arr[mid + 1])
          return {arr[mid - 1], arr[mid]};
        high = mid - 1;
      } else if(arr[mid] < target) {
        low = mid + 1;
      } else {
        high = mid - 1;
      }
    }
  return {-1, -1};
}

Время: O(log n)

Пробел: O(1)

Надеюсь, вы нашли мое объяснение полезным и что вы узнали из него что-то.