65 lines
2.1 KiB
JavaScript
65 lines
2.1 KiB
JavaScript
function getSequence(arr) {
|
||
// 用于记录每个位置的前驱索引,以便最后重建序列
|
||
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];
|
||
// 忽略值为 0 的元素(Vue源码中的diff算法对0有特定处理)
|
||
if (arrI !== 0) {
|
||
// 获取当前最长序列中最后一个元素的索引
|
||
j = result[result.length - 1];
|
||
// 贪心算法部分:如果当前元素大于当前最长序列的最后一个元素,直接添加
|
||
if (arr[j] < arrI) {
|
||
// 记录当前元素的前驱索引为 j
|
||
p[i] = j;
|
||
// 将当前元素的索引添加到 result 中
|
||
result.push(i);
|
||
continue;
|
||
}
|
||
// 二分查找部分:在 result 中寻找第一个大于等于 arrI 的元素位置
|
||
u = 0;
|
||
v = result.length - 1;
|
||
while (u < v) {
|
||
// 取中间位置
|
||
c = ((u + v) / 2) | 0;
|
||
// 比较中间位置的值与当前值
|
||
if (arr[result[c]] < arrI) {
|
||
// 如果中间值小于当前值,搜索区间缩小到 [c + 1, v]
|
||
u = c + 1;
|
||
} else {
|
||
// 否则,搜索区间缩小到 [u, c]
|
||
v = c;
|
||
}
|
||
}
|
||
// 如果找到的值大于当前值,进行替换
|
||
if (arrI < arr[result[u]]) {
|
||
// 如果 u 不为 0,记录前驱索引
|
||
if (u > 0) {
|
||
p[i] = result[u - 1];
|
||
}
|
||
// 更新 result 中的位置 u 为当前索引 i
|
||
result[u] = i;
|
||
}
|
||
}
|
||
}
|
||
// 重建最长递增子序列
|
||
u = result.length;
|
||
v = result[u - 1];
|
||
while (u-- > 0) {
|
||
// 将索引替换为对应的前驱索引
|
||
result[u] = v;
|
||
v = p[v];
|
||
}
|
||
// 返回最长递增子序列的索引数组
|
||
return result;
|
||
}
|
||
|
||
const list = [2, 1, 5, 3, 6, 4, 8, 9, 7];
|
||
const result = getSequence(list);
|
||
console.log(result); // [ 1, 3, 5, 6, 7 ] --> [ 1, 3, 4, 8, 9 ]
|