🥳前端算法面试--动态规划之最短路径-每日一练

前言

今天分享的内容是前端必问算法,没有之一--动态规划。

动态规划(Dynamic Programming, DP)是一种在数学、计算机科学和经济学领域中使用的解决复杂问题的方法。它的核心思想是将问题分解成相互依赖的子问题,然后用一个表格存储这些子问题的解,以避免重复计算。动态规划适用于具有重叠子问题和最优子结构特点的问题,通常可用于求解最优化问题。

提炼几个关键点:

  1. 重复的子问题
  2. 最优结构子问题
  3. 避免重复计算

其中,第一个点和上篇文章讲到的回溯算法一致,都是将一个大问题分解成多个重复的子问题。而后面两点就是动态规划的独有特点了。动态规划可以通过找到最优结构的子问题,从而避免重复计算

本篇文章的问题和上篇文章的问题一致,即找到方格的最短路径

假设 n*n 的方格,每个方格都有一个数字,表示这个方格的权重。现在要从(0,0)走到(n-1, n-1),有很多路径。就像下面这个九宫格,从左上角走到右下角,可以右右右下下下,也可以下下下右右右,也可以右右下下下右,有很多种走法,每种走法的权重都不一样,现在要找到权重之和最小的那条路径

问题描述摘抄自上篇文章🥳前端算法面试之找到最短路径-每日一练 - 掘金,这里还有用回溯算法解决该问题的讲解

问题分析

用回溯算法的解决思路就是找到所有的路径,但是这里还有优化的空间。

就拿上面的九宫格举例子,假设已经走到了(1,1)这个位置,那么有两种可能,一种是走(0,0)->(1,0)->(1,1),那么路径权重就是 12, 还有一种是走(0,0)->(0,1)->(1,1),这样路径权重就是 18 了。其实对于后者就没必要继续算下去了,直接使用前者的路线继续算就可以了。但对于回溯算法,每种权重还是会继续算下去,这就是回溯算法时间复杂度高的原因。

动态规划对回溯算法优化,正是从这个方向入手。走到某一格,只保留最短的权重路径,从而继续下去。直接抛弃其他的路径走法。

对于任何一格,可以往下走的选择都是要么往右,要么往下。假设现在走到了 (i,j) 格,它的上一步的路径来源也就要么来自上方(i-1,j),要么来自左方(i,j-1)。然后它只要选择这两个路径中最短权重的,加上自身的权重 weight(i,j),意思是走到了它这一格,最短路径是 weight(i,j) + min( weight(i-1,j), weight(i, j-1) )。 如果 X 不是重点的话,就相当于挑了一条最短路径继续往下走。如果 X 是重点,那么整个方格的最短路径的权重就求出来了。

到这里,思路分析得差不多了,下面看代码怎么实现的

动态规划的实现

javascript 复制代码
/**
 *
 * @param {number[][]} data
 */
const findShortPath = (data) => {
  // 初始化path数组
	const path = Array(data.length)
		.fill(0)
		.map(() => Array(data.length).fill(0));

	path[0][0] = data[0][0];

  // 初始化行
	for (let i = 1; i < data.length; i++) {
		path[0][i] = path[0][i - 1] + data[0][i];
	}

  // 初始化列
	for (let j = 1; j < data.length; j++) {
		path[j][0] = path[j - 1][0] + data[j][0];
	}

  // 计算剩下的格子
	for (let i = 1; i < data.length; i++) {
		for (let j = 1; j < data.length; j++) {
			path[i][j] = data[i][j] + Math.min(path[i - 1][j], path[i][j - 1]);
		}
	}
	// check
	console.log(path);
	console.log("the min weight is: ", path[data.length - 1][data.length - 1]);
};

代码的逻辑也是按照上面分析的来,计算每个格子的时候,值为data[i][j] + Math.min(path[i - 1][j], path[i][j - 1]),意为选取最短的路径往下走。注意⚠️,相较于回溯算法,动态规划的高明之处正在于此,只保留最短路径往下走,而不是计算所有的路径值。

在计算路径之前,先要初始化第 1 行和第 1 列的数据,方便后面的计算。

计算完成之后,函数的末尾会打印出 path,大家可以看看生成的 path 长什么样子。并且 path 最后一格的值就是我们要求的最短路径

执行代码

javascript 复制代码
const data = [
	[3, 4, 2, 1],
	[5, 2, 4, 1],
	[7, 6, 4, 3],
	[5, 9, 2, 4],
];

findShortPath(data);

输出结果:

下面测试几个简单的方格数据,方便大家判断代码的正确性:

例一

javascript 复制代码
const data = [
	[1, 2, 2],
	[1, 2, 2],
	[1, 1, 1],
];

打印结果:

例二

javascript 复制代码
const data = [
	[1, 2, 2],
	[1, 0, 2],
	[1, 0, 1],
];

打印结果:

代码正确🙆

总结

这篇文章分享了如何用动态规划的方法找到权重最短的路径,并且分析了回溯算法复杂度高的原因,从原因入手,就是动态规划提高性能的方向,即保留局部最优的路径,其他的路径可能性就不考虑。同时文中给出了 JS 代码实现,例子也很详细。

可以评论区留言哦。我每天都会分享一篇算法小练习,喜欢就点赞+关注吧

相关推荐
zhougl99637 分钟前
html处理Base文件流
linux·前端·html
花花鱼41 分钟前
node-modules-inspector 可视化node_modules
前端·javascript·vue.js
HBR666_44 分钟前
marked库(高效将 Markdown 转换为 HTML 的利器)
前端·markdown
想跑步的小弱鸡2 小时前
Leetcode hot 100(day 3)
算法·leetcode·职场和发展
careybobo2 小时前
海康摄像头通过Web插件进行预览播放和控制
前端
xyliiiiiL3 小时前
ZGC初步了解
java·jvm·算法
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
喝拿铁写前端4 小时前
字段聚类,到底有什么用?——从系统混乱到结构认知的第一步
前端
爱的叹息4 小时前
RedisTemplate 的 6 个可配置序列化器属性对比
算法·哈希算法
再学一点就睡4 小时前
大文件上传之切片上传以及开发全流程之前端篇
前端·javascript