寻找数组最大递增子序列
寻找最大递增子序列是比较常见的算法,实现方式也不尽相同,此文稍作介绍。
我是算法世界的小学生,因为最近再看 vue3 Diff 有一个寻找最大递增子序列的使用,遂为了弄明白花了些时间并去网上网罗了一些实现方法,但重点分析 这种方法 的实现思路,我认为是比较优秀的方式。
一种好的实现方式分析
这种方式找的是最大递增子序列的索引,而不是项。主要说一下思路:
- 遍历数组
- 将
arr
拷贝一份(p
),遍历过程中用来保存比当前项小的最近项的索引。 result
保存最后的返回值,遍历过程中遇到比result
最后一项大的 就push
相应的索引,否者更新result中对应项比当前项大且不相等的一项。注意是索引。- 用一个变量保存在
result
中大于当前项的索引(u
,也是找中位数的起始项索引),一个变量保存中位数索引(c
)
一个变量保存取中位数的结束项索引(v
)。 - 利用
p
修正数组
注意点:
p
每次更新都是从当前result
中找出索引对应的数组项与当前项相比,如果存在比当前项小的项,那么p
相应的项更新为找出项的在数组的索引。否者不更新。result
仅在遇到相等项时不更新,否者就更新。
文字略显干涩且绕口难以理解,来点图例解析
例子
- 为了便于理解,result和p里面保存的是项,而不是索引。则result 初始值为 [arr[0]]
- 浅蓝代表遍历的当前项
i
代表遍历的索引值
找 [2, 7, 10, 6, 3, 1, 10, 1, 1]
的最大递增子列。
i=0,
i=1,
i=2,
i=3
, 当前项 current = 6
, 6
始终和 result[c]
作比较
u=0,v=2
(result.length-1);c=1; 6<7
u=0,v=1
,c=0,6>2
u=1,v=1
,循环结束,于是result[u] = 6, p[3] = 2
总结,result中比当前项大的更新为当前项,result中比当前项小的项用来更新p相应的项。
i=4,
3>result中的2
, 将2
之后的一项更新为3
。并且将result
中的2
更新p
相应的项
i=5,
- 当前项小于
result
中的最小项,p
不做更新。result
第一项2
更新为1
i=6,
u=0, v=2
; 10>result[1]= 3;u=2
,v=2
; 10不大于result[2] = 10;且不小于10,那么 p不更新(u虽然大于0,但是10不小于result[2])。result也不更新
i=7
,
u最后=0
。1不大于result中最小项,则p不更新,又因为1不大于result最大项,result也不更新。
i=8
, 处理同 i=7
,均不更新,这里我添加了result
的对应关系
最后由 p
对 result
进行调整,从后向前遍历 result
先找 10,设10在 Arr
索引为index
,对应 p 里前一个元素(p[index])时 7
,将 result
中 10
的前一个元素3
改为 7
,
再看 7
,对应 p
中保存的前一个元素 p[1]
是 2
,则将 result
第一个元素改为 2
.
找最大递增子序列索引【完整代码】
/*
[3,1,5,4,2] => [1,2]
*/
function lis(arr) {const p = arr.slice();const result = [0]; // 索引数组let i;let j;let u;let v;let c;const len = arr.length;for (i = 0; i < len; i++) {const arrI = arr[i];if (arrI !== 0) {// 取最后一个元素j = result[result.length - 1];if (arr[j] < arrI) {p[i] = j;result.push(i);continue;}u = 0;v = result.length - 1;//result长度大于1时while (u < v) {// 取中位数c = ((u + v) / 2) | 0;if (arr[result[c]] < arrI) {u = c + 1;} else {v = c; //result中位数大于等于 当前项。v取中位数}}if (arrI < arr[result[u]]) {if (u > 0) {p[i] = result[u - 1];}result[u] = i;}}}u = result.length;v = result[u - 1];while (u-- > 0) {result[u] = v;v = p[v];}return result;
}
找最大递增子序列(含项和索引,这是我的小改版)【完整代码】
function lisItem(arr){let p = arr.slice();let result = [{ele: arr[0], idx: 0}];let j;let i;// c, u, v 是保存 result 中 的中位数、确定中位数的起始位置、确定职位数的结束位置。let u;let c;let v;const len = arr.length;for(i =0; i< len; i++){const current = arr[i];if(current !== 0) {j = result.length - 1;if(result[j].ele < current) {console.log(' > : ', i, current);result.push({ele: current, idx: i})p[i] = result[j].idx;}else {u = 0;v = result.length - 1;while(u < v) {c = (u + v) >> 1; // (prev+last)/2 | 0;if(current > result[c].ele){u = c + 1;}else {v = c;}}debugger;// 上面的while主要找出 大于等于当前项的索引值 uconsole.log('修改: ',JSON.stringify(result), u, i, current);if(current < result[u].ele) { // 不断的修正resultif(u>0) {const indexOfArr = arr.indexOf(result[u-1].ele) // 大于等于当前项的前一项的索引indexOfArr !== -1 && (p[i] = indexOfArr)}result[u] = {ele: current, idx: i}}}}}u = result.length;v = result[result.length - 1];while(--u > 0){result[u] = { ele: arr[v.idx], idx: v.idx };v = { ele: arr[p[v.idx]], idx: p[v.idx] }}return result;}
这篇文章也说的比较清晰,推荐一看 Vue3 DOM Diff 核心算法解析
其它的实现方法
来自 vue-design
const seq = [0, 8, 4, 12, 2, 10]function lis(seq) {const valueToMax = {}let len = seq.lengthfor (let i = 0; i < len; i++) {valueToMax[seq[i]] = 1}let i = len - 1let last = seq[i]let prev = seq[i - 1]while (typeof prev !== 'undefined') {let j = iwhile (j < len) {last = seq[j]if (prev < last) {const currentMax = valueToMax[last] + 1valueToMax[prev] =valueToMax[prev] !== 1? valueToMax[prev] > currentMax? valueToMax[prev]: currentMax: currentMax}j++}i--last = seq[i]prev = seq[i - 1]}const lis = []i = 1while (--len >= 0) {const n = seq[len]if (valueToMax[n] === i) {i++lis.unshift(len)}}return lis
}
最长连续递增子序列, 来自使用JS找出数组中的最长递增子串
function findL(arr){let arr_act=[];let arr_pre=[];let arr_max=[];for(var i=0;i<arr.length;i++){let val=arr[i]if(arr_act.length==0){arr_act.push(val)}else if(val<arr_act[arr_act.length-1]){arr_pre=arr_act;arr_act=[];arr_act.push(val)}else {arr_act.push(val)}if(arr_pre.length>arr_max.length){arr_max=arr_pre;}}return arr_max.length>arr_act.length?arr_max:arr_act}
最长递增子序列, 来自使用JS找出数组中的最长递增子串
function lis(arr){let arr_max=[];let cache=[];for(var i=0;i<arr.length;i++){cache.push([])}for(let i=0;i<arr.length;i++){for(let j=0;j<i;j++){if(arr[j]<arr[i]){if(cache[i].length<cache[j].length+1){cache[i]=[].concat(cache[j])}}}cache[i].push(arr[i]);if(cache[i].length>arr_max.length){arr_max=[].concat(cache[i])}}return arr_max
}
总结
基本核心要点就是比对当前项与上一项的大小,保存响应的状态最后做更新。
我说的那种方法有什么亮点呢,主要是这里
while (u < v) {// 取中位数c = ((u + v) / 2) | 0;if (arr[result[c]] < arrI) {u = c + 1;} else {v = c; //result中位数大于等于 当前项。v取中位数}
}
if (arrI < arr[result[u]]) {if (u > 0) {p[i] = result[u - 1];}result[u] = i;
}
- 与中位比大小能省去遍历项,数据量越大效果越明显。
- 用的变量非常少,思路非常凝练。值得学习的方式。
参考
【个人博客】vue-design
【CSDN】使用JS找出数组中的最长递增子串
寻找数组最大递增子序列相关推荐
- 【树状数组】递增子序列(金牌导航 数据结构优化DP-1)
递增子序列 金牌导航 数据结构优化DP-1 题目大意 给出一个序列,让你求长度为m的单调递增子序列的个数 输入样例 3 2 1 1 2 7 3 1 7 3 5 9 4 8 输出样例 2 12 数据范围 ...
- 耐心排序之最长递增子序列(LIS)
目录 一.问题引入 1.最长递增子序列(LIS) 2.问题分析 3.代码实现 4.问题思考 二.耐心排序 1.基本介绍 2.操作步骤 3.代码实现 三.俄罗斯套娃信封问题 1.题目描述 2.问题分析 ...
- 491. Increasing Subsequences 递增子序列
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 示例: 输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, 6, 7], [4, ...
- leetcode 491. 递增子序列 思考分析
题目 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 说明: 给定数组的长度不会超过15. 数组中的整数范围是 [-100,100]. 给定数组中可能包含重复数字 ...
- leetcode491. 递增子序列(回溯算法)
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 示例: 输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, 6, 7], [4, ...
- LeetCode 491. 递增子序列(回溯+判重剪枝)
1. 题目 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 示例: 输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, 6, 7] ...
- Java实现 LeetCode 491递增子序列
491. 递增子序列 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 示例: 输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, ...
- 数组字符串那些经典算法:最大子序列和,最长递增子序列,最长公共子串,最长公共子序列,字符串编辑距离,最长不重复子串,最长回文子串 (转)...
作者:寒小阳 时间:2013年9月. 出处:http://blog.csdn.net/han_xiaoyang/article/details/11969497. 声明:版权所有,转载请注明出处,谢谢 ...
- 求数组中最长递增子序列
写一个时间复杂度尽可能低的程序,求一个一维数组(N个元素)中最长递增子序列的长度. 例如: arr[] = {1, -1, 2, -3, 4, -5, 6, -7},其最长递增子序列的长度为4 如(1 ...
最新文章
- JAVA方法中的参数用final来修饰的效果
- 使用etcd+confd管理nginx配置
- 云南计算机专升本数据结构_怎么查找云南省2019年专升本计算机专业试题
- css盒子模型圆形运用,【前端】CSS3学习笔记(三)——盒子模型
- fluent design_Fluent Design单选按钮,复选框,选择框,Java菜单
- Peacock:大规模主题模型及其在腾讯业务中的应用-2015
- 普通人为什么要学习Python
- 算法真的太重要了!CSDN用动画帮你快速 get 核心原理
- HDU 1018 Big Number
- Vitamin-R for Mac(GTD工作效率管理工具)
- n分频器 verilog_verilog 语言实现任意分频
- 漫话:如何给女朋友解释灭霸的指响并不是真随机消灭半数宇宙人口的?
- Navicat 设置自动插入时间触发器
- RabbitMQ 实战教程
- ios 代码例子 卷边的翻书效果
- keras实现deblurgan-v1(图像去模糊)
- Network Battery for mac(实时网速显示和电池健康) 教程
- 心流状态---人们做事时内心的一种状态
- 用 C学习51单片机——记录 4、中断系统 (1)外部中断
- dmz和端口映射_使用DMZ主机功能代替FTP服务端口映射无法使用的问题