Peak Index in a Mountain Array

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

Есть один элемент, который можно назвать пиком. Найти индекс этого элемента.

Решение #

Тривиальное решение, которое можно написать для разогрева — пройтись по всем элементам, и найти тот, который удовлетворяет условию по определению.

Сложность — O(n). После этого спросят «а можно ли быстрее?», и ответ разумеется «можно». Задача на бинарный поиск. Попробуем описать мыслительный процесс, который помогает это понять.

Мы понимаем, что есть два тренда: восходящий и нисходящий, и есть ровно один пик, который лежит на стыке. Можно взять любые две соседние точки и задать себе вопрос — какой это тренд? Если восходящий, значит пик где-то справа, если нисходящий — значит слева.

Ровно это свойство и позволяет применить бинарный поиск, по определению.

/**
 * @param {number[]} A
 * @return {number}
 */
var peakIndexInMountainArray = function(A) {
  // границы в пределах
  // которых будем искать ответ
  let start = 0;
  let end = A.length - 1;

  // бинарный поиск двигает
  // start и end друг к другу,
  // сужая количество возможных
  // вариантов для поиска вдвое на каждом шаге
  while (start < end) {
    const middle = Math.floor((start + end) / 2);

    // т.к. минимальная длина 3 (уточнили у интервьюера?) →
    // выход за границу массива не страшен,
    // но в общем случае здесь
    // нужно быть аккуратнее (проверять edge cases):
    // если длина массива 1, то middle будет равен 0,
    // соответственно A[middle + 1] — за границей массива

    if (A[middle] < A[middle + 1]) {
      // middle оказался в восходящем тренде,
      // соответственно надо продолжать поиск справа,
      // при этом раз A[middle + 1] оказался
      // больше — значит middle + 1 ближе к пику
      start = middle + 1;
    } else {
      // middle оказался в нисходящем тренде,
      // соответственно надо продолжать поиск слева,
      // при этом раз A[middle] оказался
      // больше — значит middle ближе к пику
      end = middle;
    }
  }
  // на данном этапе A[start] > A[end]
  // и отличаются на единицу,
  // т.е. мы нашли самое начало
  // нисходящего тренда — это и есть пик
  return start;
};

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

PS. Обсудить можно в телеграм-чате любознательных программистов. Welcome! 🤗

Подписывайтесь на мой твитер или канал в телеграме, чтобы узнавать о новых разборах задач.