【数据结构与算法】三个经典案例带你了解动态规划,2024前端经典面试题整理合集

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

// 递归求取的开始时间

let start1 = Date.now()

// 递归求取第40个数的值

console.log(fibonacci1(40))

// 递归求取的结束时间

let end1 = Date.now()

// 动态规划求值的开始时间

let start2 = Date.now()

// 动态规划求取第40个数的值

console.log(fibonacci2(40));

// 动态规划求值的结束时间

let end2 = Date.now()

console.log(`

递归所用时间:${end1 - start1} ms

动态规划所用时间:${end2 - start2} ms

`);

/*

102334155

102334155

递归所用时间:2476 ms

动态规划所用时间:0 ms

*/

从结果我们能很明显地看到,仅仅第40个值对于动态规划来说根本不算什么,使用的时间几乎为0,而递归却因重复求值使用了2s以上的时间

三、案例二:寻找最大公共子串

=======================================================================

首先先来看看问题需求:这里有两个字符串,即 ravenhavoc,现在我们要封装一个函数,来获取这两个字符串的所有最大公共子串,结果就是最大的公共子串为 av,并且最大的公共子串长度为2

首先看到这个问题,我觉得一般大家想到的办法都是跟图示一样

在这里插入图片描述

但这是一种简单粗暴的方法,把它用代码实现的话,中间也会有很多的重复比较的部分,所以我们这里也可以通过动态规划来解决此办法,即创建一个表,用来记录第一个字符串的每一个字符跟第二个字符串每一个字符的比较结果,最后再通过观察表来判断最大公共子串和最大公共子串的长度

假设现在有这样两个字符串:abaccdbadacef,我们要求它俩的最大公共子串

可以先建一个 8 * 7 的表,如图所示

在这里插入图片描述

行的表头表示的是第一个字符串的第n个字符;列的表头表示的是第二个字符串第m个字符

因此行的表头或列的表头为0对应的格子应当都为0,因为字符串没有第0个字符,最少是从第1个开始的,结果如下:

在这里插入图片描述

我们先找到行表头为1的这一行从左往右看,表示拿第一个字符串的第一个字符与第二个字符串的每一个字符进行比较,若不相同,则在对应格子里填0,表示的是连续相同字符的长度为0;若相同,则先看看该格子的左上角那个格子里的数 n 是多少,然后在该格子里填 n + 1

为什么当相同时要在该格子中填入比左上角的值大1的数呢?因为左上角的格子表示的是第一个字符串当前字符的前一个字符与第二个字符串当前字符的前一个字符比较后的连续相同字符长度

我们来看一下第一行的填写过程:

在这里插入图片描述

第二行表示的是拿第一个字符串的第二个字符与第二个字符串的每个字符的比较,过程如图所示:

在这里插入图片描述

第三行表示的是拿第一个字符串的第三个字符与第二个字符串的每个字符的比较,过程如图所示:

在这里插入图片描述

在上图第三行的填写过程中,第一个字符串的第三个字符与第二个字符串的第二个字符比较相同时,我们查看了一下该格子左上角的值,即判断了第一个字符当前字符的前一个字符与第二个字符当前字符的前一个字符比较后的连续字符长度为多少

剩下的三行填写过程如下图所示:

在这里插入图片描述

最终的表格如下图所示:

在这里插入图片描述

从表中我们可以看到,最大的公共子串长度为2,一共有两个长度为2的公共子串,分别是第一个字符串的第2个字符到第3个字符和第一个字符串的第3个字符到第4个字符,即 baac

根据上面的方法,我们来用代码封装一下求取最大公共子串的函数

function publicStr(s1, s2) {

// 创建一个表

let table = []

// 记录最大的公共子串长度

let max = 0

// 子串进行比较,将表填完整

for(let i = 0; i <= s1.length; i++) {

table[i] = []

for(let j = 0; j <= s2.length; j++) {

// 若行表头或列表头为0,格子里填0

if(i == 0 || j == 0) table[i][j] = 0;

// 若字符比对不相同

else if(s1[i - 1] !== s2[j - 1]) table[i][j] = 0;

// 字符比对相同

else {

// 当前格子的值等于左上角格子的值+1

table[i][j] = table[i - 1][j - 1] + 1

// 判断max是否为最大公共子串的长度

if(table[i][j] > max) max = table[i][j]

}

}

}

// 记录所有的最大公共子串的信息

let items = []

// 遍历整个表,找到所有子串长度为max的子串的最后一个字符的索引

for(let i = 0; i < s1.length; i ++) {

let current = table[i]

for(let j = 0; j < s2.length; j ++) {

if(current[j] === max) items.push(i)

}

}

console.log(最大子串长度为${max});

console.log(长度为${max}的子串有:);

for(let i in items) {

let start = items[i] - max

console.log(${s1.slice(start, start + max)});

}

}

我们用上述例子来验证一下该函数是否正确,同时我还打印了一下表的结果,大家可以跟实例中的比对一下是否正确

let s1 = ‘abaccd’

let s2 = ‘badacef’

publicStr(s1, s2)

/* 打印结果:

最大公共子串长度为2

长度为2的子串有:

ba

ac

表:[

[0, 0, 0, 0, 0, 0, 0, 0],

[0, 0, 1, 0, 1, 0, 0, 0],

[0, 1, 0, 0, 0, 0, 0, 0],

[0, 0, 2, 0, 1, 0, 0, 0],

[0, 0, 0, 0, 0, 2, 0, 0],

[0, 0, 0, 0, 0, 1, 0, 0],

[0, 0, 0, 1, 0, 0, 0, 0]

]

*/

四、案例三:背包问题

===================================================================

背包问题也算是一个非常经典的问题,假设现在你的面前有4种珠宝,它们的重量分别为 3345 ,它们的价值分别为 4679,现在你有一个能装下重量为 8 的物品,请问你会如何挑选才能使利益最大化?

当然最简单的办法就是写出所有的组合,然后计算每种组合的价值,然后就能获得利益最大化的方案

这用递归实现是非常简单的,代码如下

// 封装一个判断大小的函数

function max(v1, v2) {

return v1 > v2 ? v1 : v2

}

// 主函数,用于判断当前背包容量下,存放某个物品的最大收益

// 参数:背包容量、存放每个物品重量的数组、存放每个物品价值的数组、物品标号

function knapsack(capacity, size, value, n) {

// 如果没有物品了或者背包没容量了,则最大收益为0

if(n == 0 || capacity == 0) return 0;

// 物品n的重量大于背包容量

else if(size[n - 1] > capacity) {

// 返回上一个物品的最大收益

return knapsack(capacity, size, value, n - 1)

}

// 物品n的重量小于背包容量

else {

// 此时有两种选择:第一种:拿该物品 ; 第二种:不拿该物品

// 我们要取其中收益最大的方案,因此用到max函数

return max(value[n - 1] + knapsack(capacity - size[n - 1], size, value, n - 1), knapsack(capacity, size, value, n - 1))

}

}

// 代码测试

let capacity = 8

let size = [3, 3, 4, 5]

let value = [4, 6, 7, 9]

let n = 4

let res = knapsack(capacity, size, value, n)

console.log(res) // 15 , 表示最大收益价值为15

正如我们文章开头所说的,这样的递归效率总归是不太高的,因此我们要将其用动态规划实现,并且我们将需求改变一下,不光要求出最大收益价值,还要知道是拿了哪几样物品。

同样的,我们先创建一个表,用来记录每一种物品在任一背包容量下的最大收益

在这里插入图片描述

很明显,当背包容量为0时,我们能获得的最大收益一定为0;表中物品编号为0的这一行全部都要填上0,因为这是我们添加的对照行,并没有编号为0的物品,因此结果如图所示:

在这里插入图片描述

现在我们从编号为1的物品开始,判断其在背包容量为 1 ~ 8 的情况下,我们能获取到的最大利益为多少。显而易见,物品1的重量为3,因此当背包容量小于3时,最大收益都为0;当背包容量大于等于3时,因为还没有考虑别的物品,因此我们能获取的最大收益就等于物品1的价值,即等于4,结果如图所示:

在这里插入图片描述

接着我们考虑编号为2的物品在背包容量为 1 ~ 8 的情况下,我们能获取到的最大利益为多少。

首先知道物品2的重量为3,因此在背包容量小于3时,我们无法放入物品2,那么此时的最大收益就等于在当前背包容量下,放入物品1的最大收益;

当背包容量大于等于3时,我们能放入物品2,因此我们现在有两种选择:第一种就是不放物品2,那么我们就只能放物品1,所以我们能获得的最大收益就等于在此背包容量下放入物品1的最大收益;第二种就是放物品2,因为我们已经放了物品2了,只剩一个物品1了,所以此时的最大收益就等于物品2的价值 + 背包剩余容量下放入物品1的最大收益。我们要取这两种情况中收益最大的方案

填表过程如下图所示:

在这里插入图片描述

接着我们又考虑编号为3的物品在背包容量为 1 ~ 8 的情况下,我们能获取到的最大利益为多少。

首先知道物品3的重量为4,因此在背包容量小于4时,我们无法放入物品3,那么我们还需要考虑的就有物品1和物品2,从上一步骤得知,物品2的最大收益时在考虑了物品1的基础上得出的,因此我们只需要考虑放入物品2的最大收益即可,那么此时的最大收益就等于在当前背包容量下,放入物品2的最大收益;

当背包容量大于等于4时,我们能放入物品4,与上一个步骤类似,我们有两种选择,即放物品3和不放物品3

填表结果如下图所示:

在这里插入图片描述

同理,最后一行的填表过程如下图所示:

在这里插入图片描述

最终的填表结果如下图所示:

在这里插入图片描述

在表中可以很明显地看到,我们在背包容量为8的情况下,能获取到的最大收益为15

此时,我们还需要倒着推回去,判断一下是拿了哪几样物品才获取到的最大收益

首先找到最大收益对应的格子为物品4,然后我们判断一下该收益是否等于前一种物品(物品3)的最大收益,若等于,则表示没有放入物品4;否则表示放入了物品4。

为什么会这样判断呢?因为我们说过,在判断一个物品在某背包容量下的最大收益时,当物品重量大于背包容量或者我们选择不放入该物品时,此时的最大收益就等于前一种物品在此背包容量下的最大收益

所以这里能判断,我们放入了物品4,则此时背包容量只剩 8 - 5 = 3,所以我们找到物品3在背包容量等于3情况下最大收益对应的格子,同样判断一下上一种物品(物品2)的最大收益是否等于此格子中的最大收益,当前判断为相等,因此我们没有放入物品3

当前背包容量仍为3,我们找到物品2在背包容量等于3情况下最大收益对应的格子,判断当前最大收益不等于上一种物品(物品1)在背包容量为3情况下的最大收益,因此我们放入了物品2

则此时背包容量为 3 - 3 = 0了,无法再放入任何物品了,所以我们就可以得出结论,我们在放入物品2和物品4的情况下收益最大,最大收益价值为15


上面讲解了背包问题的动态规划思路,下面我们用代码来实现一下

function knapsack(capacity, size, value, n) {

// 返回较大的值

function max(v1, v2) {

return v1 > v2 ? v1 : v2

}

let table = []

// 生成长度为n的表

for(let i = 0; i <= n; i++) {

table[i] = []

}

专业技能

一般来说,面试官会根据你的简历内容去提问,但是技术基础还有需要自己去准备分类,形成自己的知识体系的。简单列一下我自己遇到的一些题

  • HTML+CSS

  • JavaScript

  • 前端框架

  • 前端性能优化

  • 前端监控

  • 模块化+项目构建

  • 代码管理

  • 信息安全

  • 网络协议

  • 浏览器

  • 算法与数据结构

  • 团队管理

最近得空把之前遇到的面试题做了一个整理,包括我本人自己去面试遇到的,还有其他人员去面试遇到的,还有网上刷到的,我都统一的整理了一下,希望对大家有用。

其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器等等

由于文章篇幅有限,仅展示部分内容

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
能优化

  • 前端监控

  • 模块化+项目构建

  • 代码管理

  • 信息安全

  • 网络协议

  • 浏览器

  • 算法与数据结构

  • 团队管理

最近得空把之前遇到的面试题做了一个整理,包括我本人自己去面试遇到的,还有其他人员去面试遇到的,还有网上刷到的,我都统一的整理了一下,希望对大家有用。

其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器等等

由于文章篇幅有限,仅展示部分内容

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-2vYVe4zU-1713218058309)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值