Введение (преимущества в конце статьи) 🏂
Алгоритмы всегда были частым вопросом на фронтенд-интервью на крупных фабриках, и все часто готовятся к собеседованиям в этой области, проходя их.leetcode
Вопросы по кисти.
я собрал несколькоleetcode
середина"很有意思
И очень "高频
"Тема алгоритма, дан анализ идей (с графикой) и код.
Внимательно прочитав эту статью, я уверен, что она обязательно поможет вам на собеседовании по алгоритму! 🤠
Сумма двух чисел 🦊
❝Сложность вопроса
❞easy
, задействованные знания алгоритмов включают массивы, хеш-таблицы
Название Описание
задан массив целых чиселnums
и целевое значениеtarget
, пожалуйста, найдите тот, чья сумма является целевым значением в массиве两个
целые числа и возвращают их индексы массива.
Можно предположить, что для каждого входа будет только один ответ. Однако один и тот же элемент массива нельзя использовать дважды.
Пример:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
Анализ мыслей
Когда большинство учащихся увидят эту задачу, они обязательно подумают в глубине души: эта задача слишком проста, почему бы просто не пройти один и тот же массив в двух слоях: два слоя циклов для обхода одного и того же массива; значение первого слоя цикла прохождение записывается какa
, значение, пройденное во время цикла второго слоя, записывается какb
;подобноa+b = 目标值
,Такa
а такжеb
Соответствующий индекс массива является ответом, который нам нужен.
В этом решении нет ничего плохого, но есть ли оптимизированное решение? 🤔
Имейте в виду, что двухуровневые циклы во многих случаях означаютO(n^2)
сложность, которая может легко привести к тайм-ауту вашего алгоритма. Даже если нет тайм-аута, если вы напишите два уровня обхода, когда очевидно, что есть одноуровневое решение обхода, интервьюер значительно снизит вашу оценку впечатления. 🤒
На самом деле, в процессе обхода массива мы можем добавитьMap
Структура для хранения пройденных чисел и соответствующих им значений индексов. Затем каждый раз, когда проходится новое число, он возвращается кMap
чтобы узнатьtargetNum
Появилось ли отличие от этого номера уже в предыдущем номере. Если да, то ответ уже появился, и нам не нужно идти дальше.
Давайте используем пример в этом вопросе и картинку, чтобы проиллюстрировать идею, упомянутую выше:
- объект здесь
diffs
имитироватьmap
структура:Сначала пройдите по первому элементу массива, затемkey
2,value
Индексировать 0
- Внизу обход, я встретил 7:рассчитать
targetNum
Разница между 7 и 7 равна 2, перейдите кdiffs
Получил 2 из этогоkey
, оказалось значением, которое появилось ранее. Тогда ответ на этот вопрос отсутствует!
Код
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
const twoSum = function (nums, target) {
const diffs = {};
// 缓存数组长度
const len = nums.length;
// 遍历数组
for (let i = 0; i < len; i++) {
// 判断当前值对应的 target 差值是否存在
if (diffs[target - nums[i]] !== undefined) {
// 若有对应差值,那么得到答案
return [diffs[target - nums[i]], i];
}
// 若没有对应差值,则记录当前值
diffs[nums[i]] = i;
}
};
Сумма трех чисел 🦁
❝Сложность вопроса
❞medium
, задействованные знания алгоритма включают массивы, двойные указатели
Описание темы
дает вам содержаниеn
массив целых чиселnums
,судитьnums
Есть ли три элемента вa
,b
,c
, так чтоa + b + c = 0
. Найдите все тройки, удовлетворяющие условию и не повторяющиеся.
Примечание. Ответы не могут содержать повторяющиеся тройки.
Пример:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
Анализ мыслей
и выше两数之和
Точно так же, если вы не подумаете тщательно, самым быстрым способом может быть обход нескольких слоев. Но с извлеченными уроками мы также можем превратить задачу суммирования в задачу разности: зафиксировать одно из чисел и выяснить, есть ли в оставшихся числах два числа, которые в сумме дают это фиксированное число, равное 0.
Здесь мы используем双指针法
Чтобы решить эту проблему, по сравнению с трехслойным циклом эффективность будет значительно повышена.
❝Область применения метода двойного указателя относительно широка, как правило, его можно использовать для решения таких задач, как суммирование и размер отношения. Но есть одна предпосылка: массив должен быть упорядочен
❞
Итак, наш первый шаг — сначала отсортировать массив:
// 给 nums 排序
nums = nums.sort((a,b)=>{
return a-b
})
Затем пройдитесь по массиву и зафиксируйте текущее число для каждого пройденного числа. При этом левый указатель указывает на число сразу после числа, а правый указатель указывает на конец массива. Затем левый и правый указатели перемещаются ближе к середине:Каждый раз, когда указатель перемещается один раз, подсчитайте, равна ли сумма чисел, на которые указывают два указателя, плюс фиксированное число 0. Если да, то перед нами целевая комбинация, иначе возможны два случая:
- Сумма больше 0, что указывает на то, что число справа слишком велико, и правый указатель перемещается влево.
- Сумма сложения меньше 0, что указывает на то, что число слева слишком мало, а левый указатель перемещается вправо
Код
/**
* @param {number[]} nums
* @return {number[][]}
*/
const threeSum = function(nums) {
// 用于存放结果数组
let res = []
// 目标值为0
let sum = 0
// 给 nums 排序
nums = nums.sort((a,b)=>{
return a-b
})
// 缓存数组长度
const len = nums.length
for(let i=0;i<len-2;i++) {
// 左指针 j
let j=i+1
// 右指针k
let k=len-1
// 如果遇到重复的数字,则跳过
if(i>0&&nums[i]===nums[i-1]) {
continue
}
while(j<k) {
// 三数之和小于0,左指针前进
if(nums[i]+nums[j]+nums[k]<0){
j++
// 处理左指针元素重复的情况
while(j<k&&nums[j]===nums[j-1]) {
j++
}
} else if(nums[i]+nums[j]+nums[k]>0){
// 三数之和大于0,右指针后退
k--
// 处理右指针元素重复的情况
while(j<k&&nums[k]===nums[k+1]) {
k--
}
} else {
// 得到目标数字组合,推入结果数组
res.push([nums[i],nums[j],nums[k]])
// 左右指针一起前进
j++
k--
// 若左指针元素重复,跳过
while(j<k&&nums[j]===nums[j-1]) {
j++
}
// 若右指针元素重复,跳过
while(j<k&&nums[k]===nums[k+1]) {
k--
}
}
}
}
// 返回结果数组
return res
};
Контейнер, который удерживает наибольшую воду 🥃
❝Сложность вопроса
❞medium
, задействованные знания алгоритма включают массивы, двойные указатели
Описание темы
Вам даны n целых неотрицательных чисел a1, a2, ..., an, каждое из которых представляет точку с координатами (i, ai). Нарисуйте n вертикальных линий в координатах, двумя конечными точками вертикальной линии i являются (i, ai) и (i, 0). Найдите две из этих линий, которые вместе с осью абсцисс образуют емкость с наибольшим количеством воды.
Объяснение: Вы не можете наклонить контейнер, а значение n не меньше 2.Вертикальная линия на рисунке представляет входной массив [1,8,6,2,5,4,8,3,7]. В этом случае максимальное количество воды, которое может вместить контейнер (показано синим цветом), равно 49.
Пример:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
Анализ мыслей
Прежде всего, метод, который мы можем быстро придумать: решить два на два, чтобы рассчитать количество воды, которое можно унести. Затем постоянно обновляйте максимальное значение и, наконец, возвращайте максимальное значение.
Это решение требует двух слоев циклов, а временная сложностьO(n^2)
. Это относительно жестоко, и соответствующее暴力法
.
закон о насилии
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let max = 0;
for(let i = 0; i< height.length-1; i++) {
for(let j = i + 1; j < height.length;j++) {
let area = (j - i) * Math.min(height[i], height[j]);
max = Math.max(max, area)
}
}
return max;
}
Так есть ли лучший способ? Ответ положительный.
На самом деле немного похоже双指针
Концепция , левый указатель указывает на нижний индекс 0, правый указатель указывает наlength-1
. Затем двигайтесь от левой и правой сторон к середине, каждый раз принимая меньшее значение (потому что высота воды должна основываться на меньшем).
Если левая сторона меньше правой, тоi++
,иначеj--
(Этот шаг на самом деле состоит в том, чтобы взять большую из всех высот, мы знаем, что площадь равна长*宽
). соответствует双指针 动态滑窗
Двойной указатель Динамическое скользящее окно
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let max = 0;
let i = 0;
let j = height.length - 1;
while(i < j) {
let minHeight = Math.min(height[i], height[j])
let area = (j - i)*minHeight;
max = Math.max(max, area)
if(height[i] < height[j]) {
i++
} else {
j--
}
}
return max
};
подъем по лестнице 🎢
❝Сложность вопроса
❞easy
, задействованные знания алгоритмов включают последовательность Фибоначчи и динамическое программирование.
Описание темы
Предположим, вы поднимаетесь по лестнице. Вам нужно n шагов, чтобы добраться до крыши.
Вы можете подняться на 1 или 2 ступеньки каждый раз. Сколькими способами можно добраться до вершины здания?
Примечание. Данное n является положительным целым числом.
Пример 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
Пример 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
Анализ мыслей
Этот вопрос очень часто встречается на собеседованиях, а также является очень классическим.斐波那契数列
тип темы.
Для решения этой задачи воспользуемся алгоритмической идеей динамического программирования — ее можно разделить на несколько подзадач, а количество способов подняться по n-й лестнице равно сумме 2-х частей:
- взбираться
n−1
Количество способов подняться по лестнице. Потому что еще один шаг приведет вас к энному шагу - взбираться
n−2
Количество способов подняться по лестнице, так как еще 2 ступени приведут вас к n-й ступени
Формулу можно получить:
climbs[n] = climbs[n-1] + climbs[n-2]
При этом нужно сделать следующую инициализацию:
climbs[0] = 1;
climbs[1] = 1
Код
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
let climbs = [];
climbs[0] = 1;
climbs[1] = 1;
for(let i = 2; i<= n; i++) {
climbs[i] = climbs[i-1] + climbs[i-2]
}
return climbs[n]
};
Круговой связанный список 🍩
❝Сложность вопроса
❞easy
, задействованные знания алгоритма включают связанные списки и быстрые и медленные указатели.
Описание темы
Учитывая связанный список, определите, есть ли цикл в связанном списке.
Чтобы представить кольцо в данном связанном списке, мы используем целое число pos для обозначения позиции в связанном списке, где соединяется конец связанного списка (индексы начинаются с 0). Если pos равно -1, в списке нет циклов.
Пример 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
Пример 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
Пример 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
Анализ мыслей
链表成环
Проблема также очень классические алгоритмические проблемы, часто встречающиеся в интервью.
Обычно существует два распространенных способа решения этой проблемы:标志法
а также快慢指针法
.
подписать закон
Добавьте флаг к каждому узлу, который был пройден, и пройдите по связанному списку.Когда следующий узел был отмечен, это доказывает, что односвязный список имеет кольцо.
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
while(head) {
if(head.flag) return true
head.flag = true;
head = head.next;
}
return false
};
Быстрый и медленный указатель (метод двойного указателя)
Установите два быстрых и медленных указателя, пройдите по односвязному списку, быстрый указатель делает два шага за раз, а медленный указатель делает один шаг за раз.Если в односвязном списке есть цикл, быстрый и медленный указатели в конечном итоге будет указывать на тот же узел, в противном случае, пока быстрый указатель не укажет наnull
, быстрый и медленный указатели не могут встретиться.
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
if(!head || !head.next) {
return false
}
let slow = head, fast = head.next;
while(slow !== fast) {
if(!fast || !fast.next) return false
fast = fast.next.next;
slow = slow.next
}
return true;
};
Допустимые скобки 🍉
❝Сложность вопроса
❞easy
, задействованные знания алгоритма включают стек и хеш-таблицу.
Описание темы
Учитывая только включить'('
,')'
,'{'
,'}'
,'['
,']'
Строка , чтобы определить, является ли строка допустимой.
Действительная строка должна удовлетворять:
1. Открывающая скобка должна быть закрыта закрывающей скобкой того же типа. 2. Левые скобки должны быть закрыты в правильном порядке.
Обратите внимание, что пустые строки считаются допустимыми строками.
Пример 1:
输入: "()"
输出: true
Пример 2:
输入: "()[]{}"
输出: true
Пример 3:
输入: "(]"
输出: false
Пример 4:
输入: "([)]"
输出: false
Пример 5:
输入: "{[]}"
输出: true
Анализ мыслей
Этот вопрос можно использовать栈
структура.
Идея, вероятно, такова: при встрече с левой скобкой заталкивать ее в стек, при встрече с правой скобкой вынимать верхний элемент стека и возвращаться, если он не соответствуетfalse
, если есть совпадение, цикл продолжается.Первое решение заключается в использованииswitch case
.
switch case
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
let arr = [];
let len = s.length;
if(len%2 !== 0) return false
for(let i = 0; i<len;i++) {
let letter = s[i];
switch(letter) {
case '(': {
arr.push(letter);
break;
}
case '{': {
arr.push(letter);
break;
}
case '[': {
arr.push(letter);
break;
}
case ')': {
if(arr.pop() !== '(') return false
break;
}
case '}': {
if(arr.pop() !== '{') return false
break;
}
case ']': {
if(arr.pop() !== '[') return false
break;
}
}
}
return !arr.length
};
Во-вторых, поддерживатьmap
Объект:
хеш-таблицаmap
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
let map = {
'(': ')',
'{': '}',
'[': ']'
}
let stack = [];
let len = s.length;
if(len%2 !== 0) return false
for(let i of s) {
if(i in map) {
stack.push(i)
} else {
if(i !== map[stack.pop()]) return false
}
}
return !stack.length
};
Раздвижное окно макс. ⛵
❝Сложность вопроса
❞hard
, задействованные знания алгоритма имеют двусторонние очереди.
Описание темы
Учитывая массив nums, существует скользящее окно размера k, перемещающееся из крайнего левого края массива в крайнее правое. Вы можете видеть только k чисел в скользящем окне. Скользящее окно перемещается вправо только на одну позицию за раз.
Возвращает максимальное значение в скользящем окне.
Для продвинутых: Можете ли вы решить эту задачу за линейное время?
Пример:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
намекать:
- 1 <= nums.length <= 10^5
- -10^4 <= nums[i] <= 10^4
- 1 <= k <= nums.length
Анализ мыслей
решение грубой силы
Первый способ относительно прост. Это также метод, который может быстро придумать большинство студентов.
- перебирать массив
- Пройдите максимальное значение в каждом интервале по очереди и поместите его в массив
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function(nums, k) {
let len = nums.length;
if(len === 0) return []
if(k === 1) return nums
let resArr = []
for(let i = 0; i <= len - k;i++) {
let max = Number.MIN_SAFE_INTEGER;
for(let j = i; j < i + k; j++) {
max = Math.max(max, nums[j])
}
resArr.push(max)
}
return resArr;
};
дека
Эта проблема также может быть решена с помощью двусторонней очереди, Суть в том, что при перемещении окна обновляется только максимальное значение в соответствии с измененными элементами.
В сочетании с приведенной выше анимацией (Источник изображения) Разбираем следующие идеи:
- Проверьте элементы в конце очереди, чтобы убедиться, что все они удовлетворяют условию быть больше или равны текущему элементу. Если это так, поставьте в очередь текущий элемент напрямую. В противном случае удаляйте элементы в конце очереди один за другим, пока элемент в конце очереди не станет больше или равен текущему элементу. (Этот шаг предназначен для поддержания уменьшения очереди: убедитесь, что головной элемент очереди является максимальным значением текущего скользящего окна. Таким образом, каждый раз, когда мы берем максимальное значение, мы можем напрямую взять головной элемент очереди. очередь.)
- поставить в очередь текущий элемент
- Проверьте элемент head, чтобы убедиться, что элемент head был исключен из скользящего окна. Если это так, удалите головной элемент из очереди. (Этот шаг предназначен для обеспечения достоверности очереди: убедиться, что все элементы в очереди находятся в пределах скользящего окна.)
- Исключите частный случай, когда скользящее окно не было инициализировано и первое максимальное значение еще не появилось.
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function(nums, k) {
// 缓存数组的长度
const len = nums.length;
const res = [];
const deque = [];
for (let i = 0; i < len; i++) {
// 队尾元素小于当前元素
while (deque.length && nums[deque[deque.length - 1]] < nums[i]) {
deque.pop()
}
deque.push(i)
// 当队头元素的索引已经被排除在滑动窗口之外时
while (deque.length && deque[0] <= i - k) {
// 队头元素出对
deque.shift()
}
if (i >= k - 1) {
res.push(nums[deque[0]])
}
}
return res
};
Дневная температура 🌡
❝Сложность вопроса
❞medium
, задействованное знание алгоритма имеет стек.
Описание темы
Основываясь на списке дневных температур, создайте список заново, и выходными данными для соответствующего местоположения будет количество дней ожидания повышения температуры сверх этого дня. Если после этого он не поднимается, используйте вместо этого 0 в этой позиции.
Например, учитывая список температур = [73, 74, 75, 71, 69, 72, 76, 73], ваш вывод должен быть [1, 1, 4, 2, 1, 1, 0, 0].
Подсказка: длина списка температур находится в диапазоне [1, 30000]. Каждое значение температуры указано в градусах по Фаренгейту и является целым числом в диапазоне [30, 100].
Анализ мыслей
Увидев этот вопрос, каждый легко может подумать о методе насильственного обхода: прямой двухслойный обход, первый слой находит температуру, второй слой находит ближайшее повышение температуры к этой температуре, а затем находит соответствующие две температуры. разницы индекса достаточно.
Однако это решение требует двух уровней обхода, а временная сложностьO(n^2)
, что явно не является оптимальным решением.
Этот раздел может использовать стек для оптимизации.
Общая идея такова: поддерживать нисходящий стек. Когда пройденные температуры сохраняют монотонно убывающий тренд, выполняем операцию проталкивания индексов этих температур, пока появляется число, оно ломает этот монотонно убывающий тренд, т. е. выше предыдущего значения температуры, тогда мы вычисляем разницу между нижними индексами двух температур до и после и получаем целевую разницу между предыдущей температурой и первым повышением температуры.
Код
/**
* @param {number[]} T
* @return {number[]}
*/
var dailyTemperatures = function(T) {
const len = T.length;
const stack = [];
const res = (new Array(len)).fill(0);
for (let i=0; i < len;i++) {
while(stack.length && T[i] > T[stack[stack.length - 1]]) {
const top = stack.pop();
res[top] = i - top;
}
stack.push(i)
}
return res
};
Генерация кронштейна 🎯
❝Сложность вопроса
❞medium
, задействованные знания алгоритма включают рекурсию и поиск с возвратом.
Описание темы
Число n представляет собой логарифм создания скобок. Пожалуйста, создайте функцию, которая может генерировать все возможные и допустимые комбинации скобок.
Пример:
输入:n = 3
输出:[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
Анализ мыслей
Эта проблема решается рекурсией.
Потому что левая и правая скобки должны совпадать и закрываться. Таким образом, числа, соответствующие «(» и «)», равныn
, при выполнении этого условия рекурсия завершается, и соответствующее значение помещается в массив результатов.
Здесь есть потенциальное ограничение:有效的
Комбинация кронштейнов. Соответствующая логика заключается в том, чтобы поставить "(" или ")" в каждой позиции перед:
- Нужно определить, меньше ли число "(" n
- Число ")" меньше, чем "("
Код
/**
* @param {number} n
* @return {string[]}
*/
var generateParenthesis = function(n) {
let res = []
const generate = (cur, left, right) => {
if (left ===n && right === n) {
res.push(cur)
return
}
if (left < n) {
generate(cur + '(', left + 1, right)
}
if (right < left) {
generate(cur + ')', left, right + 1)
}
}
generate('', 0, 0)
return res
};
Буквенные комбинации телефонных номеров 🎨
❝Сложность вопроса
❞medium
, задействованные знания алгоритма включают рекурсию и поиск с возвратом.
Описание темы
Учитывая строку, содержащую только числа 2-9, вернуть все комбинации букв, которые она может представлять.
Отображение цифр в буквы дается следующим образом (так же, как клавиши телефона). Обратите внимание, что 1 не соответствует ни одной букве.Пример:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
Анализ мыслей
Сначала используйте объектmap
Сохраните отношение сопоставления между числами и буквами, затем пройдите соответствующую строку и сохраните строку в массиве результатов в первый раз.result
Во втором и последующих обходах новый массив строк генерируется путем двухслойного обхода.
Код
Послойный обход хеш-карты
/**
* @param {string} digits
* @return {string[]}
*/
var letterCombinations = function(digits) {
let res = [];
if (digits.length === 0) return []
let map = {
2: 'abc',
3: 'def',
4: 'ghi',
5: 'jkl',
6: 'mno',
7: 'pqrs',
8: 'tuv',
9: 'wxyz'
}
for (let num of digits) {
let chars = map[num]
if (res.length > 0) {
let temp = []
for (let char of chars) {
for (let oldStr of res) {
temp.push(oldStr + char)
}
}
res = temp
} else {
res.push(...chars)
}
}
return res
};
рекурсия
/**
* @param {string} digits
* @return {string[]}
*/
var letterCombinations = function(digits) {
let res = [];
if (!digits) return []
let map = {
2: 'abc',
3: 'def',
4: 'ghi',
5: 'jkl',
6: 'mno',
7: 'pqrs',
8: 'tuv',
9: 'wxyz'
}
function generate(i, str) {
let len = digits.length;
if (i === len) {
res.push(str)
return
}
let chars = map[digits[i]]
for (let j = 0; j < chars.length; j++) {
generate(i+1, str + chars[j])
}
}
generate(0, '')
return res
};
Количество островов 🏝
❝Сложность вопроса
❞medium
, используемое знание алгоритма — это DFS (поиск в глубину).
Описание темы
Учитывая двухмерную сетку «1» (суша) и «0» (вода), подсчитайте количество островов в сетке.
Острова всегда окружены водой, и каждый остров может быть образован только путем соединения соседних земель по горизонтали или по вертикали.
Также можно предположить, что меш окружен водой со всех четырех сторон.
Пример 1:
输入:
11110
11010
11000
00000
输出: 1
Пример 2:
输入:
11000
11000
00100
00011
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。
Анализ мыслей
Как показано выше, все, что нам нужно рассчитать, это количество зеленых островов, которые связаны (только горизонтально и / или вертикально) на графике.
Классический подход к этой проблеме沉岛
, общая идея такова: используйтеDFS
(поиск в глубину), если встречается 1, текущая 1 будет изменена на 0, и поиск в глубину будет выполняться на верхней, нижней, левой и правой сторонах текущей координаты и подсчитываться.
Условие завершения: за границей двумерного массива или встретить 0 , вернуться напрямую.
Код
/**
* @param {character[][]} grid
* @return {number}
*/
var numIslands = function(grid) {
const rows = grid.length;
if (rows === 0) return 0
const cols = grid[0].length;
let res = 0;
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (grid[i][j] === '1') {
helper(grid, i, j, rows, cols)
res++
}
}
}
return res
}
function helper(grid, i, j, rows, cols) {
if (i < 0 || j < 0 || i > rows - 1 || j > cols - 1 || grid[i][j] === "0")
return;
grid[i][j] = "0"
helper(grid, i + 1, j, rows, cols);
helper(grid, i, j + 1, rows, cols);
helper(grid, i - 1, j, rows, cols);
helper(grid, i, j - 1, rows, cols);
}
Раздайте печенье 🍪
❝Сложность вопроса
❞easy
, задействованное знание алгоритма является жадным алгоритмом.
Описание темы
Допустим, вы замечательный родитель и хотите угостить своих детей маленьким печеньем. Однако каждому ребенку можно дать не более одного файла cookie. Для каждого ребенка i существует значение аппетита gi, которое является наименьшим размером файла cookie, который удовлетворит аппетит ребенка, а для каждого файла cookie j существует размер sj. Если sj >= gi , мы можем назначить этот файл cookie j дочернему элементу i, и этот дочерний элемент будет удовлетворен. Ваша цель — удовлетворить как можно больше детей и вывести это максимальное значение.
Уведомление:
Вы можете предположить, что значение аппетита положительное. Ребенок может иметь не более одного файла cookie.
Пример 1:
输入: [1,2,3], [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
Пример 2:
输入: [1,2], [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.
Анализ мыслей
Эта тема является типичной贪心算法
Добрый. Решение проблемы примерно такое:
- Приоритет удовлетворения потребностей детей с низким аппетитом
- Пусть максимальное количество детей, которое может быть удовлетворено, равно
maxNum = 0
- Маленькие аппетиты требуют маленьких, большие аппетиты требуют больших
- Решите с обеих сторон, затем по одной
- когда
饼干j
>=胃口i
час,i++
,j++
,maxNum++
- когда
饼干j
<胃口i
Когда бисквитов недостаточно, замените их на более крупные.j++
- когда
- После границы остановиться
Код
/**
* @param {number[]} g
* @param {number[]} s
* @return {number}
*/
var findContentChildren = function(g, s) {
g = g.sort((a, b) => a - b)
s = s.sort((a, b) => a - b)
let gLen = g.length,
sLen = s.length,
i = 0,
j = 0,
maxNum = 0;
while (i < gLen && j < sLen) {
if (s[j] >= g[i]) {
i++;
maxNum++
}
j++
}
return maxNum
};
Лучшее время для покупки и продажи акций II 🚁
❝Сложность вопроса
❞easy
Используемые знания алгоритмов включают динамическое программирование и жадные алгоритмы.
Описание темы
Дан массив, i-й элемент которого — цена данной акции в i-й день.
Разработайте алгоритм для расчета максимальной прибыли, которую вы можете получить. Вы можете совершить столько сделок, сколько сможете (покупая и продавая акции несколько раз).
Уведомление:Вы не можете участвовать в нескольких сделках одновременно (вы должны продать предыдущую акцию, прежде чем покупать ее снова).
Пример 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
Пример 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
Пример 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
намекать:
- 1 <= prices.length <= 3 * 10 ^ 4
- 0 <= prices[i] <= 10 ^ 4
Анализ мыслей
На самом деле идея этого вопроса относительно проста:
- поддерживать переменную
profit
хранить прибыль - Поскольку вы можете покупать и продавать несколько раз, цена на задней должна быть больше, чем цена на передней, тогда вы можете покупать и продавать.
- Следовательно, пока
prices[i+1] > prices[i]
, затем наложитьprofit
- обход завершен
profit
максимальная прибыль
Код
/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(prices) {
let profit = 0;
for (let i=0; i < prices.length - 1; i++) {
if (prices[i+1] > prices[i]) profit += prices[i+1] - prices[i]
}
return profit
};
Разные пути 🛣
❝Сложность вопроса
❞medium
, знание алгоритма, связанное с динамическим программированием.
Описание темы
Робот расположен в верхнем левом углу сетки m x n (начальная точка отмечена как «Старт» на изображении ниже).
Робот может двигаться вниз или вправо только на один шаг за раз. Робот пытается достичь правого нижнего угла сетки (помеченного «Готово» на изображении ниже).
В. Сколько всего существует различных путей?Например, изображение выше представляет собой сетку 7 x 3. Сколько существует возможных путей?
Пример 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
Пример 2:
输入: m = 7, n = 3
输出: 28
Анализ мыслей
Это видно из вопроса: робот может двигаться только на один шаг вправо или вниз, тогда движение из левого верхнего угла в правый нижний угол = общее количество путей справа + общее количество путей из дно.
Таким образом, динамическое уравнение может быть получено как:dp[i][j] = dp[i-1][j]+dp[i][j-1]
.
Код
❝используется здесь
❞Array(m).fill(Array(n).fill(1))
Инициализируется, потому что у каждого квадрата есть хотя бы один ход.
/**
* @param {number} m
* @param {number} n
* @return {number}
*/
var uniquePaths = function(m, n) {
let dp = Array(m).fill(Array(n).fill(1))
for (let i = 1; i < m;i++) {
for (let j = 1; j< n; j++) {
dp[i][j] = dp[i-1][j] + dp[i][j-1]
}
}
return dp[m-1][n-1]
};
Изменить 💰
❝Сложность вопроса
❞medium
, знание алгоритма, связанное с динамическим программированием.
Описание темы
Даны монеты разного номинала и общая сумма. Напишите функцию для вычисления минимального количества монет, необходимого для получения общей суммы. Возвращает -1, если ни одна из комбинаций монет не составляет общую сумму.
Пример 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
Пример 2:
输入: coins = [2], amount = 3
输出: -1
проиллюстрировать:Вы можете придумать бесконечное количество монет каждого типа.
Анализ мыслей
Мы также используем эту тему动态规划
решать.
Предполагая, что даны монеты разного номинала [1, 2, 5] и цель равна 60, какое минимальное количество монет требуется?
Сначала нам нужно разложить подзадачи и найти оптимальную подструктуру иерархически.
❝❞
dp[i]
: Указывает, что общая суммаi
Количество монет для оптимального решения при
Давайте подумаем: сколько существует способов найти общую сумму 60? Есть 3 способа, потому что у нас есть 3 монеты разного номинала.
- Возьмите монету достоинством 1 + общее количество монет для оптимального решения 59. То есть: дп[59] + 1
- Берем монету номиналом 2 + количество монет для оптимального решения всего 58. То есть: дп[58] + 1
- Возьмите монету номиналом 5 + общее количество монет для оптимального решения 55. То есть: дп[55] + 1
Следовательно, оптимальное решение с общей суммой 60 является оптимальным решением среди трех вышеупомянутых решений, то есть с наименьшим количеством монет.Давайте используем код, чтобы выразить это ниже:
dp[60] = Math.min(dp[59] + 1, dp[58] + 1, dp[55] + 1);
выведен状态转移方程
:
dp[i] = Math.min(dp[i - coin] + 1, dp[i - coin] + 1, ...)
❝в
❞coin
Сколько возможностей, нам нужно сравнить, сколько раз пройденоcoins
Массивы, вы можете сравнить их по отдельности
Код
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var coinChange = function(coins, amount) {
let dp = new Array(amount+1).fill(Infinity)
dp[0] = 0;
for (let i=0;i<= amount;i++) {
for (let coin of coins) {
if (i - coin >= 0) {
dp[i] = Math.min(dp[i], dp[i-coin]+1)
}
}
}
return dp[amount] === Infinity ? -1 : dp[amount]
};
Благосостояние
Большинство студентов, изучающих интерфейс, затрудняются систематически изучать алгоритмы.Здесь я составил карту ума, которая представляет собой всесторонний обзор системы алгоритмов интерфейса.
Я также поддерживаюgithub
склад:https://github.com/Jack-cool/js_algorithm
, который содержит большое количествоleetcode
Проблема решена, и она все еще обновляется.star
какие! 🤗
В этой статье используетсяmdniceнабор текста