最常公共子序列
算法原理
代码 最常公共子序列.js
var f = longCommonSubsequence("abcde", "ace"); console.log(f); function longCommonSubsequence(text1, text2) { let m = text1.length; let n = text2.length; let f = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0)); console.log(f); for (i = 1; i <= m; i++) { let a1 = text1[i - 1]; for (j = 1; j <= n; j++) { let a2 = text2[j - 1]; if (a1 === a2) { f[i][j] = f[i - 1][j - 1] + 1; } else { f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]); } } } return f; }
01背包问题
算法原理
原理图
视频讲解
代码
代码目录
-
前i -1个物品的组合价值
-
仍然是前i-1个物品的组合价值,准确的说去掉当前物品容量的前i-1个物品的组合价值
-
加上当前物品的价值
var dp = backPack( [ [2, 3], [3, 4], [4, 5], [5, 6], ], 8 ); console.log(dp); function backPack(arr, capacity) { let [weight, value] = arr[0]; // m表示背包中物品的数量 let m = arr.length; let dp = new Array(m + 1).fill(0).map(() => new Array(capacity + 1).fill(0)); for (i = 1; i <= m; i++) { let [weight, value] = arr[i - 1]; for (j = 1; j <= capacity; j++) { if (weight > j) { dp[i][j] = dp[i - 1][j]; } else { // 不放当前物品,就是前n个物品的最佳组合 // 放当前物品,就是背包容量减去当前物品重量,即 j - weight // 然后再加上物品的价值,即 + value dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight] + value); } } } return dp; }
输出效果
贪心算法
分配饼干问题
题目描述
有一群孩子和一堆饼干,每个孩子有一个饥饿度,每个饼干都有一个大小。每个孩子只能吃
一个饼干,且只有饼干的大小不小于孩子的饥饿度时,这个孩子才能吃饱。求解最多有多少孩子
可以吃饱。
代码
-
当饼干的饱腹度大于小孩的饥饿度时,此时这个孩子吃饱了,于是孩子指针就得下移一位 child++;
-
当饼干的饱腹度不能满足孩子时,此时饼干不能满足,于是饼干指针就得往下移一位
let filledChildren = greedy([1, 3], [1, 2, 3]); console.log(filledChildren); /** * 贪心的策略是:尽量使用 饱腹度最小 的饼干满足 饥饿度最小 的孩子 * 所以这里很显然的就会用到排序,然后从小到大的往上试 */ function greedy(children, cookies) { // 对数组进行从小到大的排序 children.sort((m, n) => m - n); cookies.sort((m, n) => m - n); let child = 0; let cookie = 0; while (child < children.length && cookie < cookies.length) { // 当饼干的饱腹度大于小孩的饥饿度时,cookies[cookie] >= children[child] // 此时这个孩子吃饱了 // 于是孩子指针就得下移一位 child++; // 当饼干的饱腹度不能满足孩子时,此时饼干不能满足, // 于是饼干指针就得往下移一位,cookies[cookie++] if (cookies[cookie++] >= children[child]) { child++; } } return child; }
无重叠区间
题目
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
示例 1:
输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释:
移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: [ [1,2], [1,2], [1,2] ]
输出: 2
解释:
你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:
输入: [ [1,2], [2,3] ]
输出: 0
解释:
你不需要移除任何区间,因为它们已经是无重叠的了。
解题思路
本题解决一个很经典的贪心算法问题 Interval Scheduling(区间调度问题)。给你很多形如 [start, end] 的闭区间,请你设计一个算法,算出这些区间中最多有几个互不相交的区间。这个问题在生活中的应用广泛,比如你今天有好几个活动,每个活动都可以用区间 [start, end] 表示开始和结束的时间,请问你今天最多能参加几个活动呢?显然你一个人不能同时参加两个活动,所以说这个问题就是求这些时间区间的最大不相交子集。
解题思路分三步:
(1)从区间集合 intvs 中选择一个区间 x,这个 x 是在当前所有区间中结束最早的(end 最小)。
(2)把所有与 x 区间相交的区间从区间集合 intvs 中删除。
(3)重复步骤 1 和 2,直到 intvs 为空为止。之前选出的那些 x 就是最大不相交子集。
可以按每个区间的 end 数值升序排序:
算法原理
[注]图片来自公众号:labuladong
代码
-
对这些区间[start,end],按照end进行升序排序
-
比较前后区间的end值,如果后面区间的start 小于前面区间的start值,说明区间重叠
let count = eraseOverlapIntervals([ [3, 4], [2, 3], [1, 2], [1, 3], ]); console.log(count); function eraseOverlapIntervals(arr) { // 对区间[start,end],按照end进行升序排序 arr.sort(([, n], [, y]) => { return n - y; }); let count = 0; let [, base_end] = arr[0]; for (let i = 1; i < arr.length; i++) { let [start, end] = arr[i]; // 后面区间的开始小于前面区间的结束,start < base_end // 则区间重叠 if (start < base_end) { count++; } else { base_end = end; } } return count; }
递归和回溯
全排列问题
原理图一
原理图二
算法框架
回溯算法框架。解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题:
1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。
如果你不理解这三个词语的解释,没关系,我们后面会用「全排列」和「N 皇后问题」这两个经典的回溯算法问题来帮你理解这些词语是什么意思,现在你先留着印象。
代码方面,回溯算法的框架:
result = [] def backtrack(路径, 选择列表): if 满足结束条件: result.add(路径) return for 选择 in 选择列表: 做选择 backtrack(路径, 选择列表) 撤销选择
其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」,特别简单。
代码
console.log(permute([1, 2, 3])); function permute(nums) { // 结果集 let res = []; // 路径 let path = []; // 已使用的数字 let used = []; backtracking(nums, used); function backtracking(nums, used) { // 路径的长度等于数组的长度,遍历完成,退出 if (path.length === nums.length) { res.push(Array.from(path)); return; } for (let i = 0; i < nums.length; i++) { // 如果该数字已经被使用,则跳过 if (used[i]) continue; path.push(nums[i]); // 当前元素推入path used[i] = true; // 标志位设置为true,表示该元素以遍历 backtracking(nums, used); path.pop(); used[i] = false; } } return res; }
合并排序
merge()原理图
-
A1和A2比较,谁小,谁就写入A.
-
与此同时, 写入和被写入的下标加一移动
-
哨兵,最后用一个很大的数,用作哨兵,这样就是不用在去判断这两个数组的长度了.
merge_sort()原理图
代码
function merge(Arr, start, mid, end) { let Arr1 = Arr.slice(start, mid); let Arr2 = Arr.slice(mid, end); // 数组的会后一位,添加一个哨兵 Arr1.push(Number.MAX_SAFE_INTEGER); Arr2.push(Number.MAX_SAFE_INTEGER); // x 表示原数组的写入下标,所以起始值为 x =start // 循环结束的条件,就是 写入下标小于这个end,即 x<end // y 和 z 分别表示Arr1 和 Arr2 的下标 for (let x = start, y = 0, z = 0; x < end; x++) { Arr[x] = Arr1[y] < Arr2[z] ? Arr1[y++] : Arr2[z++]; } return Arr; } function merge_sort(Arr, start, end) { // 递归终止条件 if (end - start < 2) return; // 取中间值,向下取整 let mid = Math.ceil((start + end) / 2); merge_sort(Arr, start, mid); merge_sort(Arr, mid, end); merge(Arr, start, mid, end); } let Arr = [2, 3, 52, 23, 2, 456, 74]; merge_sort(Arr, 0, Arr.length); console.log(Arr);
自然合并排序_待填写
待完成代码1
/** * 若数组a中元素为{4,8,3,7,1,5,6,2},则自然排好序的子数组段有{4,8},{3,7},{1,5,6},{2}, * 经一次合并后得到2个合并后的子数组段{3,4,7,8}和{1,2,5,6}, * 继续合并相邻排好序的子数组段,直至整个数组已排好序,最终结果{1,2,3,4,5,6,7,8} */ let Arr = [4, 8, 3, 7, 1, 5, 6, 2]; / function merge_pass(Arr) { let sortedArrOfStart = [0]; let sortedCount = 1; let begin = Arr[0]; for (let i = 1; i < Arr.length; i++) { if (begin > Arr[i]) { sortedArrOfStart[sortedCount++] = i; } begin = Arr[i]; } return sortedArrOfStart; }
待完成代码2
let Arr = [4, 8, 3, 7, 1, 5, 6, 2]; let splitArr = [0, 2, 4, 7]; /** * 若数组a中元素为{4,8,3,7,1,5,6,2},则自然排好序的子数组段有{4,8},{3,7},{1,5,6},{2}, * 经一次合并后得到2个合并后的子数组段{3,4,7,8}和{1,2,5,6}, * 继续合并相邻排好序的子数组段,直至整个数组已排好序,最终结果{1,2,3,4,5,6,7,8} */ merge_nature(Arr, splitArr); function merge_nature(Arr, splitArr) { let start = splitArr[0]; let end = splitArr[1]; let mid = splitArr[2]; merge(Arr, start, mid, end); merge_nature(Arr); return Arr; } function merge(Arr, start, mid, end) { let Arr1 = Arr.slice(start, mid); let Arr2 = Arr.slice(mid, end); // 数组的会后一位,添加一个哨兵 Arr1.push(Number.MAX_SAFE_INTEGER); Arr2.push(Number.MAX_SAFE_INTEGER); // x 表示原数组的写入下标,所以起始值为 x =start // 循环结束的条件,就是 写入下标小于这个end,即 x<end // y 和 z 分别表示Arr1 和 Arr2 的下标 for (let x = start, y = 0, z = 0; x < end; x++) { Arr[x] = Arr1[y] < Arr2[z] ? Arr1[y++] : Arr2[z++]; } console.log(Arr); return Arr; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战