阅读 108

菜鸟前端今日份leetCode算法学习QAQ

半年工作经验前端备战2022上半年跳槽-算法学习日记

18届某渣本软件工程专业毕业。

稀里糊涂考土木研究生,稀里糊涂复试被踹。

20年疫情家里蹲了大半年,经过大学室友引路自学小半年前端21年3月深圳入职,工作经验从0到现在的5个月零26天,打算22年年后跳一个大一点的厂子。

新的一周,组里的pm上周跑路了,需求不明确暂时没业务写,摸鱼1天自己学习。

开局先甩一个最近收藏的算法学习好文,记录一下,今天学习的主要内容也出自该文,剩余内容也会认真看完。

# 写给前端的算法进阶指南,我是如何两个月零基础刷200题

两个数组的交集

描述问题内容: 给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
示例 2:

输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]

复制代码

来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/in…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

1. 文中给出了一种利用hash表的解法,双函数-时间复杂度:O(n+m)


let intersect = function(nums1, nums2) {
   let map1 = makeCountMap(nums1)
   let map2 = makeCountMap(nums2)
   let res = []
   for(let num of map1.keys()) {
       const count1 = map1.get(num)
       const count2 = map2.get(num)
       if(count2) {
           // 如果2中确实出现了1中的num那么就取出现次数较少的为计数 然后按照计数push该计数次数的该元素
           const pushCount = Math.min(count1, count2)
           for(let i = 0; i < pushCount; i++) {
               res.push(num)
           }
       }
   }
   return res
} 

function makeCountMap(nums) {
    let map = new Map()
    for(let i = 0; i < nums.length; i++) {
        let num = nums[i]
        let count = map.get(num)
        if(count) {
            map.set(num, count + 1)
        } else {
            map.set(num, 1)
        }
    }
    return map
}
复制代码
这里给出另一种单函数hash表解法-时间复杂度:O(n+m)
//思路:遍历短数组建造hash表计数,然后遍历长数组,做消消乐~

let intersect = function (nums1, nums2) {
    // 保持短的数组放在更前面 
    if (nums1.length > nums2.length) {
        return intersect(nums2, nums1)
    }
    let map = new Map()
    // 遍历短数组 建立每个值的出现次数
    for(let i = 0; i < nums1.length; i++) {
        let count = (map.get(nums1[i]) || 0) + 1
        map.set(nums1[i], count)
    }
    // 开辟栈记录交集
    let res = []
     //遍历长数组 与 短数组对照 
    for (let i = 0; i < nums2.length; i++) {
        let count = map.get(nums2[i]) || 0
        // map结构中记录的对应的数据有次数就压入结果栈 再更新map结构中记录短数组的次数(做减法)
        if(count) {
            res.push(nums2[i])
            map.set(nums2[i], count - 1)
        }
    }
    return res 
}
console.log(intersect([1, 2, 3], [1, 2])); // [1, 2]
复制代码

2. 双指针+排序

   说实话我自己之前不知道双指针具体做法是啥(太菜了太菜了TT)
   今天在leetCode上看到了,这里用自己的话叙述一遍:
    
   首先要对数组进行一个升序排列
   利用  Arr.prototype.sort()
   该方法可以传入一个比较函数形如(a, b) => a - b;
    1,若a-b<0,a会排在b前
    2,若a-b=0,返回0
    3,若a-b>0, b会排在a前
    
   
    对于进行比较的的arr1 = [a1, a2, a3...]和 arr2 = [b1, b2, b3...]
    开辟一个记录栈 res = []
    设置两个指针 i 和 j 分别记录当前指向arr1和 arr2的元素
    1,如果当前比较的两元素相等,那么把该元素压入记录栈,i和j会同时向后指一位
    2,如果当前比较的两元素不相等,那么其中较小值对应的指针会向后指一位
    依照上述两点执行直到其中一个数组遍历完毕,这时候对于长数组而言的多余元素其实是unique的,不予理会,
    此时res就是想要的交集
    
复制代码

OK,现在列出解答

let intersect = function (nums1, nums2) {
    //排序
    nums1.sort((a, b) => a - b)
    nums2.sort((a, b) => a - b)
    // 构建指针和记录栈
    let i = 0, j = 0, res = []
    //遍历
    while(i < nums1.length && j < nums2.length) {
        if(nums1[i] === nums2[j]) {
            res.push(nums1[i])
            i++
            j++
        } else nums1[i] < nums2[j] ? i++ : j++
    }
    return res 
}

console.log(intersect([1, 2, 3], [2, 3]));  // [2, 3]
复制代码
然后还看到进阶的双指针+归并排序
let intersect = function(nums1, nums2) {
    nums1 = nums1.sort((a,b) => a - b);
    nums2 = nums2.sort((a,b) => a - b);
    let i = 0;
    let j = 0;
    let k = 0;
    while(i < nums1.length && j < nums2.length){
        if(nums1[i] < nums2[j]){
            i++;
        }else if(nums1[i] > nums2[j]){
            j++;
        }
        else{
            nums1[k++] = nums1[i];
            i++;
            j++;
        }
    }
    return nums1.slice(0,k);
};
复制代码

这里对比普通排序的不同在于nums1[k++] = nums1[i]; ,假如i=0,j=0的指针指向的两值相同,设为x,会把x直接保存给nums1[0]( 因为此时k是0),然后k会自增, nums1nums2的指针同时后跳

按照上述的方法继续遍历执行到完毕,最后slice(0, k)返回需要的结果

好处是不需要开辟新的记录栈,减少了空间复杂度

贴一个阮老师的ES6-Map和Set数据结构 es6.ruanyifeng.com/#docs/set-m…

3.暴力循环-时间复杂度O(n^2)

let intersect = function (nums1, nums2) {
    let result = []
    let minArr = nums1.length > nums2.length ? nums2 : nums1
    let maxArr = nums1.length > nums2.length ? nums1 : nums2
    for( let i = 0; i < minArr.length; i++ ) {
        let maxIndex = maxArr.indexOf(minArr[i])
        if(maxIndex != -1) {
            result.push(maxArr.splice(maxIndex, 1)[0])
        }
    }
    return result
}
复制代码

4.贴一个时间复杂度和空间复杂度的解读

读完就差不多懂了 # 算法的时间与空间复杂度(一看就懂)

本文原创发布于微信公众号「 不止思考 」,欢迎关注,交流更多的 互联网认知、工作管理、大数据、Web、区块链技术。

小结

今天学习的内容其实并不难,但是对我这种前端小菜鸡而言确实获益良多。

  1. 学习了算法的思路,

  2. 复习巩固了各种数组方法和Set,Map数据结构,

  3. 还有诸如

 if(nums1[i] === nums2[j]) {
            res.push(nums1[i])
            i++
            j++
        } else nums1[i] < nums2[j] ? i++ : j++
复制代码
            nums1[k++] = nums1[i];
            i++;
            j++;
复制代码

较为优雅的细节写法

尾声

最近玩的有点疯,10.1还要回家赴大学同学的婚宴,

希望自己9月可以不忘初心,努力进步哈哈哈哈哈哈哈

尽管开始会很慢,但是至少我在做,我做了,

不积跬步无以至千里,不积小流无以成江海。









    
复制代码
文章分类
前端