vue3最长递增子序列看不懂来砍我

43 阅读3分钟

二分查找

// 二分查找 首先先学习下
// 一般的二分查找 查下10在数组中的位置

function binarySearch(arr, num) {
    let start = 0;
    let end = arr.length - 1
    while(start<end) {
        let mid = Math.floor((start + end) / 2);
        if(arr[min] === num) return mid
        if(arr[mid]<num) {
            start = mid + 1
        }else{
            end = mid
        }
    }
}

insterIndex([2, 5, 7, 9, 10, 20], 10)

二分查找 找出第一个比 他大的数

// 这个是查找比他大的第一个数 思路差不多一样 不断缩小范围 最后的 start就是第一个比他大的数 // [2, 5, 7, 9, 10, 20] 现在有个数 11 看看他应该是要插到数组的哪个位置上

function insterIndex(arr, num) {
    // 首先就要从 中间开始找   如果中间的数比11小的话  就从右边开始找   继续中间一个数对比
    // 否则就是从左边一半  开始
    let start = 0
    let end = arr.length - 1
    while(start<end) {
        let mid = Math.floor((start + end) / 2);
        if(arr[mid]<num) {
            // 中间的数 比11 小   那就说明左边的数都比11小 不用看了   因为是递增的序列
            start = mid + 1
        }else{
            end = mid
        }
    }
    return start
}  

insterIndex([2, 5, 7, 9, 10, 20], 11)

最长递增子序列个数

// 接下来要掌握的  就是求最大子序列的 个数了
function getSequenceLength(arr) {
    let len = arr.length;
    let result = [arr[0]];
    for (let i = 1; i < len; i++) {
        const arrI = arr[i];
        let rsLastNum = result[result.length - 1];
        if (rsLastNum < arrI) {
            result.push(arrI)
            continue;
        }
        start = 0;
        end = result.length - 1
        while (start < end) {
            middle = (start + end) / 2 | 0;
            if (result[middle] < arrI) {
                start = middle + 1;
            } else {
                end = middle
            }
        }
        if (arrI < result[start]) {
            result[start] = arrI
        }
    }
    console.log(result)
    return result.length
}

let len = getSequenceLength([2, 5, 8, 4, 6, 7, 9, 3]);

// [2, 3, 6, 7, 9]  最后的 result 是这个  虽然不是正确的递增序列  正确的应该是[2, 4, 6, 7, 9]   但是子序列但是个数是正常的
// 这个是一个贪心算法   默认第一个就是最小值  
// [2]
// 遍历到 5  5>2   rs = [2, 5]
// 遍历到 8  8>5   rs = [2, 5, 8]
// 遍历到 4  4<8   要利用二分查找找到第一个比4大的数  也就是5 替换他的位置  [2, 4, 8]
// 遍历到 6  6<8   要利用二分查找找到第一个比6大的数  也就是8 替换他的位置  [2, 4, 6]
// 遍历到 7  7>6   rs = [2, 4, 6, 7]
// 遍历到 9  9>7   rs = [2, 4, 6, 7, 9]
// 遍历到 3  3<9   要利用二分查找找到第一个比3大的数  也就是4 替换他的位置  [2, 3, 6, 7, 9]

最长递增子序列个数 但是记录的是下标

// 3 接下来这个是 下标版的   思想都是一样的 只不过是存的是index下标
// 也就是返回的是[2, 3, 6, 7, 9]的下标    [0, 7, 4, 5, 6]
function getSequenceIndex(arr) {
    let len = arr.length;
    let result = [0];
    for (let i = 0; i < len; i++) {
        const arrI = arr[i];
        resultLastIndex = result[result.length - 1];
        if (arr[resultLastIndex] < arrI) {
            result.push(i)
            continue;
        }
        start = 0;
        end = result.length - 1
        while (start < end) {
            middle = (start + end) / 2 | 0;
            if (arr[result[middle]] < arrI) {
                start = middle + 1;
            } else {
                end = middle
            }
        }
        if (arrI < arr[result[start]]) {
            result[start] = i
        }
    }
    console.log(result)
    return result.length
}

let len2 = getSequenceIndex([2, 5, 8, 4, 6, 7, 9, 3]);
// 先举个通俗易懂的例子   看下  
// 不懂的话  看下下面 回头在看下这个例子
// 场景就是 现在一排学生 从里面抽最少的人 让他能够 从低到高排列  

// 小明2  小红5  小强6  小溪3  小敏8 小伟4

// 那一个本子记下 let p = [0,0,0,0,0,0]
// 结果  let rs = [小明2]

// 现在开始遍历  下标为1   是小红5
// 小红比小明高  rs = [小明2, 小红5]
// 小红现在要知道  他的上一个人是小明  那么记下p p[1] = 小明2  现在p = [0,小明2,0,0,0,0]


// 现在开始遍历  下标为2   是小强6
// 小强比小红高  rs = [小明2, 小红5, 小强6]
// 那小强就记住上一个人是小红  那么记下p p[2] = 小红5  现在p = [0,小明2,小红5,0,0,0]


// 现在开始遍历  下标为3   是小溪3
// 小溪比小强矮  替换小明的位置  rs = [小溪3, 小红5, 小强6]
// 那小溪上一个人没人  那么记下p p[3] = undefined  现在p = [0,小明2,小红5,undefined,0,0]


// 现在开始遍历  下标为4   是小敏8
// 小强比小红高  rs = [小溪3, 小红5, 小强6, 小敏8]
// 那小敏8就记住上一个人是小强  那么记下p  p[4] = 小强6  现在p = [0,小明2,小红5,undefined,小强6,0]


// 现在开始遍历  下标为5   是小伟4
// 小伟4比小敏8矮  替换小溪3的位置  rs = [小伟4, 小红5, 小强6, 小敏8]
// 那小伟4上一个人没人  那么记下p p[5] = undefined  现在p = [0,小明2,小红5,undefined,小强6,undefined]

// 遍历完了现在 
// 但是现在的队伍不一定正确   但是最后一个人位置是肯定正确的  也就是说小敏位置绝对是最后一个
// 实在不行在拿一个本子记下
// rs2 = [小敏8] 
// 我们知道小敏的下标是4   从p中寻找p[4] = 小强6
// rs2 = [小强6, 小敏8] 
// 然后小强6的小标是  2    从p中寻找p[2] = 小红5
// rs2 = [小红5, 小强6, 小敏8] 
// 然后小红5的小标是  1    从p中寻找p[1] = 小明2
// rs2 = [小明2, 小红5, 小强6, 小敏8]   
// 这就是最终的排序 
// 从[小伟4, 小红5, 小强6, 小敏8]  到 [小明2, 小红5, 小强6, 小敏8]  

最长递增子序列完整版

function getSequence(arr) {
    let len = arr.length;
    let result = [0];
    let resultLastIndex;
    let start;
    let end;
    let middle;
    let p = arr.slice(0); // 用来标识索引的
    for (let i = 0; i < len; i++) {
        const arrI = arr[i];
        if (arrI !== 0) {// 这个是vue中的 特殊处理  0可以不管
            resultLastIndex = result[result.length - 1];
            if (arr[resultLastIndex] < arrI) {
                result.push(i)
                p[i] = resultLastIndex; // 让当前最后一项记住前一项的索引
                continue;
            }
            start = 0;
            end = result.length - 1
            while (start < end) {
                middle = (start + end) / 2 | 0;
                if (arr[result[middle]] < arrI) {
                    start = middle + 1;
                } else {
                    end = middle
                }
            }
            if (arrI < arr[result[start]]) {
                result[start] = i
                p[i] = result[start - 1]; // 记住换的那个人的前一项的索引
            }
        }
    }
    // 追溯 
    let i = result.length;// 获取数组长度
    let last = result[i - 1]; // 最后一项的索引
    while (i-- > 0) {
        result[i] = last; // 用最后一项的索引来追溯
        last = p[last]; // 用p中的索引来进行追溯
    }
    return result
}
let result = getSequence([2, 5, 8, 4, 6, 7, 9, 3]);

// 2, 5, 8, 4, 6, 7, 9, 3

// result 是来记录结果
// p是用来追溯的   

// 第一次   循环  i = 0
// 遍历的是  2    当前的result [0]  result[length-1] 也是等于2 第一次的时候 没做啥处理 走个场子

// 第二次   循环  i = 1
// 遍历的是  5    5 比  result[length-1]大 也就是5比2大   
// result 变成了 [0, 1]
// p 记录下  上一个老大的位置   p[i] = 0  也就是2的位置 [2,0,8,4,6,7,9,3]

// 第三次   循环  i = 2
// 遍历的是  8    8 比  result[length-1]大 也就是8比5大   
// result 变成了 [0, 1, 2]
// p 记录下  上一个老大的位置   p[i] = 1  也就是之前5的位置  [2,0,1,4,6,7,9,3]


// 第四次   循环  i = 3
// 遍历的是  4    4 比  result[length-1]小 也就是4比8小   
// 4 要从 result里面利用二分法查找  当然result是索引  真正的值在arr这边   找到第一个比4大的值 替换成4的索引 
// result 记录下4的位置  [0, 3, 2]
// p记录下4前面一个人的索引  3是4目前的位置 那么他前面的索引就是0    p变成 [2, 0, 1, 0, 6, 7, 9, 3]


// 第五次   循环  i = 4
// 遍历的是  6   6 比  result[length-1]小 也就是6比8小   
// 6 要从 result里面利用二分法查找  当然result是索引  真正的值在arr这边   找到第一个比6大的值 替换成6的索引 
// result 记录下4的位置  [0, 3, 4]
// p记录下6前面一个人的索引  4是6目前的位置 那么他前面的索引就是3    p变成 [2, 0, 1, 0, 3, 7, 9, 3]


// 第六次   循环  i = 5
// 遍历的是  7    7 比  result[length-1]大 也就是7比6大   
// result 变成了 [0, 3, 4, 5]
// p 记录下  上一个老大的位置   p[i] = 4    [2, 0, 1, 0, 3, 4, 9, 3]

// 第七次   循环  i = 6
// 遍历的是  9    9 比  result[length-1]大 也就是9比7大   
// result 变成了 [0, 3, 4, 5, 6]
// p 记录下  上一个老大的位置   p[i] = 5    [2, 0, 1, 0, 3, 4, 5, 3]


// 第八次   循环  i = 7
// 遍历的是  3    3 比  result[length-1]小 也就是3比9小   
// 3 要从 result里面利用二分法查找  当然result是索引  真正的值在arr这边   找到第一个比3大的值 替换成3的索引 
// result 记录下4的位置  [0, 7, 4, 5, 6]
// p记录下3前面一个人的索引  7是3目前的位置 那么他前面的索引就是0    p变成 [2, 0, 1, 0, 6, 7, 9, 0]

// 现在到了 最后的  回溯
// 不管怎么说中间的顺序如何变化   但是最大的那个人 一定是最后一个   而且每个人都记住他前一个人的位置  所以按这样找就能梳理出正确的顺序
// 现在最后的一项 索引是 6    result[4] = 6   [0, 7, 4, 5, 6]
// 从p中 查找 p[6] = 5  得知6的上一个是5  
// result[3] = 5   [0, 7, 4, 5, 6]
// 从p中 查找 p[5] = 4  得知5的上一个是4
// result[2] = 4   [0, 7, 4, 5, 6]
// 从p中 查找 p[4] = 3  得知4的上一个是3
// result[1] = 3   [0, 3, 4, 5, 6]
// 从p中 查找 p[3] = 0  得知3的上一个是0
// result[0] = 0   [0, 3, 4, 5, 6]
// 循环结束