Я задал этот вопрос во время интервью, и, несмотря на то, что он застрял на нем (T_T), он теперь находится в моем коротком списке фаворитов, потому что я нашел, что мне сложно и действительно удовлетворяет, наконец, решить! Проблема прошла примерно так (по своим собственным словам):
Объем воды
Вам дают множество неотрицательных целых чисел, представляющих карту возвышения. Представьте, что высоты, представленные этими целыми числами, являются физическими холмами и долинами, и когда в долинах будет накапливаться вода. Рассчитайте и верните одно целочисленное значение, которое представляет максимальный объем воды, который может накапливаться.
Например:
Учитывая массив [2, 5, 4, 0, 3, 1, 6, 2, 1, 3], функция должна вернуть 15. Ниже приведено визуальное представление карты возвышения:
X X - - - - X X X - - - X X X - X - X - - X X X X - X - X X - X _X_X_X_-_X_X_X_X_X_X_ 2 5 4 0 3 1 6 2 1 3
Думайте о x как о высотах, и о приливах, когда уровень воды заполняет пустые пространства. Вы увидите, что всего пятнадцать бросков, и это число, которое мы заинтересованы в расчете.
Мой подход
Сначала я мог задуматься только о решении только с точки зрения итерации «горизонтально» через массив и суммирование вертикальных пробелов. Я попытался найти первую самую высокую высоту, а затем следующую, и попытался объяснить пространства между ними. Таким образом можно решить, но лично я обнаружил, что этот подход является чрезмерно сложным и запутанным с точки зрения реализации – я продолжал спотыкаться о себе.
Но!
Мой момент «ага» произошел, когда я наконец увидел его «вертикально», и вместо этого он итерал сверху вниз, суммируя вдоль горизонтальной оси визуализированной карты возвышения.
Попробуйте решить его самостоятельно! Какой подход работает лучше всего для вас?
Мое решение
1. Сначала найдите максимальную высоту в массиве и установите переменную «высота тока», равную ей. Кроме того, инициализируйте возвращаемое значение в переменной «громкости».
const elevationMap = [2, 5, 4, 0, 3, 1, 6, 2, 1, 3]; function findVolume (heights) { let volume = 0; let currentHeight = Math.max(...heights); return volume; }
2. Начиная с текущего (самого высокого уровня), найдите другие индексы со значениями на этой высоте, чтобы мы могли определить, где пробелы между ними. Мы будем проходить путь от максимальной высоты до самого низкого уровня, и я буду использовать цикл while вместо цикла для читаемости, но либо сработает. Мы определим пару вспомогательных функций как можно более описательно:
function findVolume (heights) { let volume = 0; let currentHeight = Math.max(...heights); while (currentHeight > 0) { const indicesAtHeight = findIndicesAtOrAboveHeight(currentHeight, heights); const additionalVolume = determineVolumeAtHeight(indicesAtHeight); volume += additionalVolume; currentHeight--; } return volume; }
3. Наша первая вспомогательная функция найдет все индексы массива высоты со значениями на нашей нынешней высоте или выше:
findIndicesAtOrAboveHeight = (height, allHeights) => { let relevantIndices = []; allHeights.forEach((h, idx) => { if (h >= height) { relevantIndices.push(idx); } }); return relevantIndices; }
4. Следующая вспомогательная функция примет наше массив индексов на текущей высоте и составит количество пустых пространств между ними. Нам даже не нужно обращать внимание на более широкий массив высот здесь, мы можем просто добавить разницу между значениями последовательного индекса (я пытался назвать вещи здесь, чтобы сделать его более понятным, но полное решение в конце будет более кратким)
determineVolumeAtHeight = (indices) => { let volAtHeight = 0; for (let i = 0; i < indices.length - 1; i++) { const currentIndex = indices[i]; const currentIndexPlusOne = indices[i]+1; const nextIndex = indices[i+1]; if (nextIndex !== currentIndexPlusOne) { volAtHeight += (nextIndex - currentIndex - 1); } } return volAtHeight; }
5. Наш цикл должен продолжаться до тех пор, пока текущая высота не достигнет нуля, а затем мы можем просто вернуть значение громкости.
Все вместе сейчас
Решение, описанное выше, будет выглядеть так, когда все будет составлено:
function findVolume (heights) { let volume = 0; let currentHeight = Math.max(...heights); findIndicesAtOrAboveHeight = (height, allHeights) => { let relevantIndices = []; allHeights.forEach((h, idx) => { if (h >= height) { relevantIndices.push(idx); } }); return relevantIndices; } determineVolumeAtHeight = (indices) => { let volAtHeight = 0; for (let i = 0; i < indices.length - 1; i++) { if (indices[i+1] !== indices[i]+1) { volAtHeight += indices[i+1] - indices[i] - 1; } } return volAtHeight; } while (currentHeight > 0) { let indicesAtHeight = findIndicesAtOrAboveHeight(currentHeight, heights); let additionalVolume = determineVolumeAtHeight(currentHeight, indicesAtHeight); volume += additionalVolume; currentHeight--; } return volume; }
Заворачивать
Это решение выполняет работу, но оно, безусловно, может быть оптимизировано. Вы можете сделать это наоборот, суммируя вертикально, а не горизонтально по уровню высоты, или вы можете представить рекурсию, чтобы сделать ее более кратким. Я не буду справляться с ними здесь, но я хотел бы услышать о других подходах, которые могут работать хорошо. Спасибо за чтение!
Оригинал: “https://dev.to/asterids/practice-problem-water-volume-4ec0”