源码
为了获取最优的最长递增子序列,根据leetcode 300. 最长递增子序列获取的并不一定是排序最小的序列也可通过,而贪心(其实就像数组[10,9,2,5,3,7,101,18],[2,3]肯定比[2,5]好) + 二分法,使获得的上升幅度最小,序列最长。
方法所在库传送门:github.com/vuejs/core/…
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
function getSequence(arr: number[]): number[] {
const p = arr.slice()
const result = [0]
let i, j, u, v, 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
while (u < v) {
c = (u + v) >> 1
if (arr[result[c]] < arrI) {
u = c + 1
} else {
v = c
}
}
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 getLongestIncreasingSubsequence(arr) {
// 复制一份数组,用于存储序列索引
let p = arr.slice();
let result = [0]; // 最长递增子序列(存储的索引下标)
let ai, ri, left, right, mid;
for (ai = 0; ai < arr.length; ai++) {
const arrItem = arr[ai];
if (arrItem !== 0) {
// 拿【序列索引】最大值
ri = result[result.length - 1];
// 大于【序列索引下最大值】直接添加
if (arr[ri] < arrItem) {
p[ai] = ri; // 存储【序列索引】到【复制的数组】下
result.push(ai);
continue;
}
// 当前循环值小于【序列索引下最大值】,需要把其插入到序列内
// 二分法
left = 0;
right = result.length - 1;
// 得到当前循环值所在【序列最小值】的位置
while (left < right) {
mid = (left + right) >> 1; // 二分下标值
if (arr[result[mid]] < arrItem) {
// 数组值大于序列二分值,left向右移(二分下标值+1)个位置,继续二分
left = mid + 1;
} else {
// 否则,right左移到二分下标值,继续二分
right = mid;
}
}
// 二分定位的下标值left 可能大于等于数组值,也可能小于数组值
// 数组值小于left值,则存储序列的第(left - 1)位的下标
if (arrItem < arr[result[left]]) {
// left值必须要大于0
if (left > 0) {
// 将【序列(left - 1)索引】存于【复制的数组】下
// 建立和 left 位的索引
p[ai] = result[left - 1]; // 这时ai坐标位的值就应该是对应上一个序列索引
}
result[left] = ai;
}
}
}
left = result.length;
right = result[left - 1]; //取最后一个值,其当前已是最长序列,但不一定是最长递增值最优序列
while (left-- > 0) {
// 倒序根据在数组p里的索引添加
result[left] = right;
right = p[right]; // 获取存储在p里的序列索引值
}
return result;
}