You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
引言(文末有福利)🏂
算法一直是大厂前端面试常问的一块,而大家往往准备这方面的面试都是通过
leetcode
刷题。我特地整理了几道
leetcode
中「很有意思
」而且非常「高频
」的算法题目,分别给出了思路分析(带图解)和代码实现。认真仔细的阅读完本文,相信对于你在算法方面的面试一定会有不小的帮助!🤠
两数之和 🦊
题目描述
给定一个整数数组
nums
和一个目标值target
,请你在该数组中找出和为目标值的那两个
整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
思路分析
大多数同学看到这道题目,心中肯定会想:这道题目太简单了,不就两层遍历嘛:两层循环来遍历同一个数组;第一层循环遍历的值记为
a
,第二层循环时遍历的值记为b
;若a+b = 目标值
,那么a
和b
对应的数组下标就是我们想要的答案。这种解法没毛病,但有没有优化的方案呢?🤔
要知道两层循环很多情况下都意味着
O(n^2)
的复杂度,这个复杂度非常容易导致你的算法超时。即便没有超时,在明明有一层遍历解法的情况下,你写了两层遍历,面试官也会对你的印象分大打折扣。🤒其实我们可以在遍历数组的过程中,增加一个
Map
结构来存储已经遍历过的数字及其对应的索引值。然后每遍历到一个新数字的时候,都回到Map
里去查询targetNum
与该数的差值是否已经在前面的数字中出现过了。若出现过,那么答案已然显现,我们就不必再往下走了。我们就以本题中的例子结合图片来说明一下上面提到的这种思路:
这里用对象
![](https://camo.githubusercontent.com/ea1e7be2807953f906bd2a8ad704e13047984cf0bba4592bdf8df4599df88c36/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634313835373835332d30312e706e67)
diffs
来模拟map
结构:首先遍历数组第一个元素,此时
key
为 2,value
为索引 0往下遍历,遇到了 7:
![](https://camo.githubusercontent.com/dcc2f210523ebdbce482a19e72f32a6aee339a83d2fb03aad91c31ad380047e9/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323034353534362d30322e706e67)
计算
targetNum
和 7 的差值为 2,去diffs
中检索 2 这个key
,发现是之前出现过的值。那么本题的答案就出来了!代码实现
三数之和 🦁
题目描述
给你一个包含
n
个整数的数组nums
,判断nums
中是否存在三个元素a
,b
,c
,使得a + b + c = 0
。请你找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。
示例:
思路分析
和上面的
两数之和
一样,如果不认真思考,最快的方式可能就是多层遍历了。但有了前车之鉴,我们同样可以把求和问题变为求差问题:固定其中一个数,在剩下的数中寻找是否有两个数的和这个固定数相加是等于 0 的。这里我们采用
双指针法
来解决问题,相比三层循环,效率会大大提升。因此我们的第一步就是先将数组进行排序:
然后对数组进行遍历,每遍历到哪个数字,就固定当前的数字。同时左指针指向该数字后面的紧邻的那个数字,右指针指向数组末尾。然后左右指针分别向中间靠拢:
![](https://camo.githubusercontent.com/283bd4528e57b79cd5bdab419403a5ed2cc7234d5933b33e01a4215d5d4c3413/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323331383837322d30312e706e67)
每次指针移动一次位置,就计算一下两个指针指向数字之和加上固定的那个数之后,是否等于 0。如果是,那么我们就得到了一个目标组合;否则,分两种情况来看:
代码实现
盛最多水的容器 🥃
题目描述
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
![](https://camo.githubusercontent.com/632e12b1804c87b25812a50f90ee2ad8dff77561a90115472c231d039c2e89c8/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323336343737362d30312e6a7067)
图中垂直线代表输入数组[1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
思路分析
首先,我们能快速想到的一种方法:两两进行求解,计算可以承载的水量。 然后不断更新最大值,最后返回最大值即可。
这种解法,需要两层循环,时间复杂度是
O(n^2)
。这种相对来说比较暴力,对应就是暴力法
。暴力法
那么有没有更好的办法呢?答案是肯定有。
其实有点类似
![](https://camo.githubusercontent.com/c746052f501330af1fb6c432e9fe780ad35137a19a903e0c3fc0649c71c1669e/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323338363533312d30322e706e67)
双指针
的概念,左指针指向下标 0,右指针指向length-1
。然后分别从左右两侧向中间移动,每次取小的那个值(因为水的高度肯定是以小的那个为准)。如果左侧小于右侧,则
i++
,否则j--
(这一步其实就是取所有高度中比较高的,我们知道面积等于长*宽
)。对应就是双指针 动态滑窗
双指针 动态滑窗
爬楼梯 🎢
题目描述
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
示例 2:
思路分析
这道题目是一道非常高频的面试题目,也是一道非常经典的
斐波那契数列
类型的题目。解决本道题目我们会用到动态规划的算法思想-可以分成多个子问题,爬第 n 阶楼梯的方法数量,等于 2 部分之和:
n−1
阶楼梯的方法数量。因为再爬 1 阶就能到第 n 阶n−2
阶楼梯的方法数量,因为再爬 2 阶就能到第 n 阶可以得到公式:
同时需要做如下初始化:
代码实现
环形链表 🍩
题目描述
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
示例 2:
示例 3:
思路分析
链表成环
问题也是非常经典的算法问题,在面试中也经常会遇到。解决这种问题一般有常见的两种方法:
标志法
和快慢指针法
。标志法
给每个已遍历过的节点加标志位,遍历链表,当出现下一个节点已被标志时,则证明单链表有环。
快慢指针(双指针法)
设置快慢两个指针,遍历单链表,快指针一次走两步,慢指针一次走一步,如果单链表中存在环,则快慢指针终会指向同一个节点,否则直到快指针指向
null
时,快慢指针都不可能相遇。有效的括号 🍉
题目描述
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串,判断字符串是否有效。有效字符串需满足:
1、左括号必须用相同类型的右括号闭合。
2、左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
示例 2:
示例 3:
示例 4:
示例 5:
思路分析
这道题可以利用
栈
结构。思路大概是:遇到左括号,一律推入栈中,遇到右括号,将栈顶部元素拿出,如果不匹配则返回
![](https://camo.githubusercontent.com/e71352051ba4e3298b57214a4a15d44436182b9c7800634ff2ec09536fcb14fe/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323539323133352d30312e706e67)
false
,如果匹配则继续循环。第一种解法是利用
switch case
。switch case
第二种是维护一个
map
对象:哈希表
map
滑动窗口最大值 ⛵
题目描述
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
进阶:你能在线性时间复杂度内解决此题吗?
示例:
提示:
思路分析
暴力求解
第一种方法,比较简单。也是大多数同学很快就能想到的方法。
双端队列
这道题还可以用双端队列去解决,核心在于在窗口发生移动时,只根据发生变化的元素对最大值进行更新。
结合上面动图(图片来源)我们梳理下思路:
每日温度 🌡
题目描述
根据每日气温列表,请重新生成一个列表,对应位置的输出是需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
思路分析
看到这道题,大家很容易就会想到暴力遍历法:直接两层遍历,第一层定位一个温度,第二层定位离这个温度最近的一次升温是哪天,然后求出两个温度对应索引的差值即可。
然而这种解法需要两层遍历,时间复杂度是
O(n^2)
,显然不是最优解法。本道题目可以采用栈去做一个优化。
![](https://camo.githubusercontent.com/780afd7e219e2a3edef1b787b3cc2ea91899dded611376a02b72f2e359525606/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323736323934372d30312e706e67)
大概思路就是:维护一个递减栈。当遍历过的温度,维持的是一个单调递减的态势时,我们就对这些温度的索引下标执行入栈操作;只要出现了一个数字,它打破了这种单调递减的趋势,也就是说它比前一个温度值高,这时我们就对前后两个温度的索引下标求差,得出前一个温度距离第一次升温的目标差值。
代码实现
括号生成 🎯
题目描述
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:
思路分析
这道题目通过递归去实现。
因为左右括号需要匹配、闭合。所以对应“(”和“)”的数量都是
n
,当满足这个条件时,一次递归就结束,将对应值放入结果数组中。这里有一个潜在的限制条件:
有效的
括号组合。对应逻辑就是在往每个位置去放入“(”或“)”前:代码实现
电话号码的字母组合 🎨
题目描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
![](https://camo.githubusercontent.com/0004626883805ddbf1534d4ce64af3ce0653712c7b803ff6ae8a04a8f66bc757/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323832343438302d30312e706e67)
示例:
思路分析
首先用一个对象
![](https://camo.githubusercontent.com/d4dbc428e4abca685a5676c6224fa1ce1226312bf26c7c201547253bf1f876d3/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323834313232382d30322e706e67)
map
存储数字与字母的映射关系,接下来遍历对应的字符串,第一次将字符串存在结果数组result
中,第二次及以后的就双层遍历生成新的字符串数组。代码实现
哈希映射 逐层遍历
递归
岛屿数量 🏝
题目描述
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
示例 2:
思路分析
如上图,我们需要计算的就是图中相连(只能是水平和/或竖直方向上相邻)的绿色岛屿的数量。
这道题目一个经典的做法是
沉岛
,大致思路是:采用DFS
(深度优先搜索),遇到 1 的就将当前的 1 变为 0,并将当前坐标的上下左右都执行 dfs,并计数。终止条件是:超出二维数组的边界或者是遇到 0 ,直接返回。
代码实现
分发饼干 🍪
题目描述
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
注意:
你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。
示例 1:
示例 2:
思路分析
这道题目是一道典型的
贪心算法
类。解题思路大概如下:maxNum = 0
饼干j
>=胃口i
时,i++
、j++
、maxNum++
饼干j
<胃口i
时,说明饼干不够吃,换更大的,j++
代码实现
买卖股票的最佳时机 II 🚁
题目描述
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
示例 2:
示例 3:
提示:
思路分析
其实这道题目思路也比较简单:
profit
用来存储利润prices[i+1] > prices[i]
,那么就去叠加profit
profit
就是获取的最大利润代码实现
不同路径 🛣
题目描述
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
![](https://camo.githubusercontent.com/f33504e8683dec84a55ea3979d14bfd819d7d150386a75881c3132b2aa23be53/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323935313735392d30312e706e67)
例如,上图是一个 7 x 3 的网格。有多少可能的路径?
示例 1:
示例 2:
思路分析
由题可知:机器人只能向右或向下移动一步,那么从左上角到右下角的走法 = 从右边开始走的路径总数+从下边开始走的路径总数。
![](https://camo.githubusercontent.com/b3d369db038aa761dc16f63a32cf8fcc1eabd0bbd423dea3160d783aa25d2873/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634323937333835342d30322e706e67)
所以可推出动态方程为:
dp[i][j] = dp[i-1][j]+dp[i][j-1]
。代码实现
零钱兑换 💰
题目描述
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
示例 2:
说明:
你可以认为每种硬币的数量是无限的。
思路分析
这道题目我们同样采用
![](https://camo.githubusercontent.com/1d0321245dac565e8e0bc6adefda0801655517de34a6a155188b6c7368e817b9/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634333030343132362d30312e706e67)
动态规划
来解决。假设给出的不同面额的硬币是[1, 2, 5],目标是 60,问最少需要的硬币个数?
我们需要先分解子问题,分层级找最优子结构。
我们想一下:求总金额 60 有几种方法?一共有 3 种方式,因为我们有 3 种不同面值的硬币。
所以,总金额为 60 的最优解法就是上面这三种解法中最优的一种,也就是硬币数最少的一种,我们下面用代码来表示一下:
推导出
状态转移方程
:代码实现
福利
大多数前端同学对于算法的系统学习,其实是比较茫然的,这里我整理了一张思维导图,算是比较全面的概括了前端算法体系。
![](https://camo.githubusercontent.com/1c7dcd6e7207ab4b49e8b9d73381b4661abde6c4222cec4c4b9fc1d7af096805/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f436f73656e39352f6173736574732f323032302d31302d31342f313630323634333131353337382d2545362539352542302545362538442541452545372542422539332545362539452538342545342542382538452545372541452539372545362542332539352e706e67)
另外我还维护了一个
github
仓库:https://github.com/Cosen95/js_algorithm
,里面包含了大量的leetcode
题解,并且还在不断更新中,感觉不错的给个star
哈!🤗❤️ 爱心三连击
1.如果觉得这篇文章还不错,来个分享、点赞、在看三连吧,让更多的人也看到~
2.关注公众号前端森林,定期为你推送新鲜干货好文。
3.特殊阶段,带好口罩,做好个人防护。
![](https://camo.githubusercontent.com/b126eb3e6a7f2904a2227f62f7e428d70c0db3f30bdf038ab92a305915d4b2de/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f4a61636b2d636f6f6c2f6173736574732f323032302d392d362f313539393339393130303633372d254535253839253844254537254142254146254536254133254145254536253945253937254535253835254143254534254243253937254535253846254237254534254241253843254537254242254234254537254130253831322e706e67)
The text was updated successfully, but these errors were encountered: