力扣刷题算法笔记(javascript版)

Posted 十九万里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣刷题算法笔记(javascript版)相关的知识,希望对你有一定的参考价值。

本文为js版本算法视频笔记 找了很多算法这个up主讲的真的非常好 适合小白去听 算法都是力扣难度中等和简单的
b站视频链接

1、岛屿最大面积(求两数最大和)


解析:提供一个数组,给了一个target 要求是在数组中找到两个数的和为target

思路:
1.创建一个map
2、for循环遍历nums数组
3、用target减nums[i]以计算哪个数跟当前数字相加等于target
4、检查map里有没有这个数,有的话就放回结果,没有就把num[I]当做key。 i单做value放入map中

代码实现:

var towSum = function(nums, target){
    const map = new Map();
    for (let i = 0; i <nums.length;i++){
        const complement = target-nums[i];
        if(map.has(complement){
            return [map.get(complement).i];
        }else {
            map.set(num[i],i);
        }
        
    }
    return [];
};

2、两链表相加

视频链接

关键的地方处理好进位的问题。两个链表相加会生成一个新的链表
代码实现:

// 两数相加
 var addTwoNumbers = function (l1,l2){
    //  定义一个新的节点
     let dummy = new listNode();
    //  curr是用来遍历列表用的
     let curr = dummy;
      let carry = 0; //处理进位用
      while(l1 !== null || l2 !== null){
          let sum = 0;
          if(l1 !==null{
              sum += l1.val;
              l1 = l1.next;
          }
          if(l2 !== null){
              sum +=l2.val;
              l2= l2.next;
          }
        
          sum += carry; //检carry有没有进位
          curr.next = new listNode( sum %10); //把得到的sum值各位作为新的list的节点
          carry = Math.floor(sum/10); //如果有carry值 就暂存carry的值 carry的值是sum/10之后取整
      }
      if(carry>0){
        curr.next = new listNode(carry);// 如果最后的carry值大于0,需要单独做一个新节点
      }
      return dummy.next;//最后return出去

 }

3、找到无重复字符的最长子串

思路:
1、创建一个set 用来存放新的字符串 还需要知道这个字符串的长度
2、两个指针第一个指针指向字符串的开头-j,第二个随着for循环遍历字符串-i
3、如果set里面有s【i】,则从set里开始伤处s【j】,并且递增j,在检查set里是否有s【j】,如此反复直到set里没有s【i】为止
5、重复步骤3和4直到完成遍历整个字符串

·代码实现:

var lengthOfLongSubstring = function(s){
    const set = new Set();
    let i = 0;
    let j = 0;
    maxLength = 0;
    if(s.length === 0){
        return 0;
    }
    for (i; i<s.length;i++){
        if (!set.has(s[i])){
            set.add(s.[i]);
            maxLength = Math.max(maxLength,set.size);
        }else{
            while(set.has(s[i])){
                set.delete(s[j]);
                j++;
            }
            set.add(s[i]);
        }
    }
    return maxLength;
}

4 求三数之和

求数组里面找三个数相加等于0 答案里面必须是唯一的
求解思路:
1、给数组排序
2、遍历数组,从0遍历到length-2
3、如果当前的数字等于前一个数字,则跳过这个数字(去重)
4、如果数字不同,则设置start = i +1,
end =length -1,查看i start和end三个数的和比零大还是小 如果比0小 start++ 如果比0大 end–;如果等于0 则把这三个数加到结果中 继续下一次遍历
5、返回结果 不能重复

代码实现:

var  threeSum = function (nums){
    const result = [];
    nums.sort(function(a,b){
        return a -b;
    })
    for(let i =0;i<length-2;i++){
        //去重 b重复的就不需要在判断
        if(i === 0 || nums[i] !== nums[i-1]){
            let start = i+1;
            let end = nums.length -1;
            while(start<end){
                //三种结果 大于等于小于0
                if(nums[i] + nums[start] + nums[end]   === 0){
                  result.push ([nums[i],nums[start],nums[end]]);
                  start ++;
                  end--;  
                  //判断是否重复
                  while(start <end && nums[start] === nums[start -1]){
                      start ++;
                  }
                  while(start <end && nums[start] === nums[start +1]){
                    end --;
                }

                }else if(nums[i] + nums[start] + nums[end]   < 0){
                    start ++;
                }else{
                    end --;
                }

            }
        }
    } 
    return result;
}

5、最长回文字符串

思想是中心扩散的思想
遍历每一个字符 把每一个字符当做中心点像两边扩散看两边是否相等,知道不相等为止
回文字符串有两种情况:中间一个不同 两边相同如abcacba a为中心点
另外一个是abccba 两边都相等 没有中间点直接像两边扩散

解题思想:
1、如果字符串长度小于2 直接返回原字符串
2、定义两个变量 一个start存储当前找到的最大会问字符串的起始位置,另一个maxLength记录字符串的长度 终止位置就是start+maxLength
3、创建一个helper function 判断左边和右边是否越界 同时左边的字符串是否等于右边的字符串,如果以上三个条件都满足,则判断是否需要更新回文字符串最大长度以及字符串的起始位置,然后将left-- right++,继续判断 直到不满足三个条件之一。
4遍历字符串 每个位置调用helper function两遍
第一遍检查i-1,i+1,第二遍检查i i+1(两种回文)

代码实现:

var longestPalindrome = function(s){
    //如果之后一个字符直接返回
    if(s.length<2){
        return s;
    }
    let start = 0;
    let maxLength = 1; //当有两个字符的时候,返回的回文字符串至少是其中一共字符 所以length应该初始化为1
    //helper函数因为要被反复用到 所以这点提出来
    function expandAroundCenter(left,right){
        //上述提到的三个条件同时满足
        while(left > =0 && right <s.length && s[left] === s[right]){
            //比较字符串长度 如果新的字符串比之前的大 则进行更新
            if(right - left + 1 >maxLength){
                        maxLength = right -left +1;
                        start = left
            }
            left --;
            right ++;
        }
    }
    // 遍历的时候把每一个当做中心 这里需要考虑两种回文字符串 因此需要在循环中执行两次函数
    for(let i = 0; i < s.length; i++){
        expandAroundCenter(i-1,i+1);
        expandAroundCenter(i ,i+1);
    }
    //最后放回的是截取的字符串
    return s.substring(start.start+maxLength);
}

6、删除链表的倒数第N个节点

题目描述:给定一个链表 删除链表的倒数第n个节点 并返回链表的头节点

思路 使用双指针的思路定位到我们要删除的元素


单项链表是 走到3的时候直接指向5 不通过4了


边界问题 如果之后1个节点
代码实现:

var  removeNthFromEnd = function (head, n){
    let dummy = new ListNode();
    dummy.next = head;
    let n1 = dummy;
    let  n2 = dummy;
    for (let i=0;i<=n;i++){
        n2 = ne.next;
    }
    while(n2.next !== null){
        n1 = n1.next;
        n2 = n2.next;
    }
    n1.next = n1.next.next;
    return dummy.next;
}

7、有效的括号

括号应该是正括号和反括号相对应的 怎么找出错误的括号
实现思路:
利用到栈的概念
1、创建一个HsapMap,把括号配对放进去,
2、创建一个stack (array)for循环遍历字符串
对于每一个字符,如果map里有这个key 那说明他是一个左括号 从map里取得对应的右括号,把他push进stack里面 否则的话他就是右括号,需要pop出stack李的第一个字符然后看他是否等于当前字符 如果不相等 则返回false
3、循环结束 如果stack不为空 则说明还剩余一些左括号,没有被闭合 返回false 否则返回true
代码实现:

var  isValid = function(s){
    //创建函数把配对情况放进去

    const mappings = new Map();
    mappings.set ("(",")");
    mappings.set("[","]");
    mappings.set("{","}");
    //创建一个栈
    const stack = [];
    for(let i = 0; i<s.lenght;i++){
        //如果mapping里有key的话就要从mappings里面取得这个i push到stack里面
        if(mappings.has(s[i])){
            stack.push(mappings.get(s[i]));
        }else{
            //先判断他是否等于i 如果不等于的话返回false
            if(stack.pop() !== s[i]){
                return false;
            }
        } 
    }
    //循环结束之后需要检查stack是否为空 不为空返回false
    if(stack.length !== 0){
        return false;
    }
}

8、合并两个有序链表

题目描述:将两个有序链表合并为一个新的有序链表并返回 新链表是通过拼接给定的两个链表的所有节点组成的

思路总结:
两个数组 开一个新的数组 在两个数组的开头放一个i和j 找到比较小的push到新数组里面 i++或者J++ 在比较两个数 小的push到新数组直到结束;
链表中只有next操作,所以有局限性 要注意这个点所以需要使用dummy来进行空的占位

代码实现:


var mergeTwoLists = function(l1,l2){
    //定义一个头部空节点 用他来生成结果 通过next串成一个链表
    let curr = new listNode();
    let dummy = curry;// 
    //对两个链表进行循环比较 需要判断两个链表不为空
    while (l1 !== null && l2 !== null ){
        if(li.val <l2.val){
            curr.next = l1;
            l1 = l1.next;//这一步相当于i++的操作 链表这样表示
        }else{
            curr.next = l2;
            l2 = l2.next;
        }
        //上面的操作新链表德 指针没有动 我们需要手动移动指针.下次添加的时候就继续从后面添加
        curr = curr.next
    }
    // 其中一共链表为空的时候 把剩余链表加到后面
    if(l1 !==null){
        curr.next = l1;
    }
    if(l2 !==null){
        curr.next = l2;
    }
    //第二个指针 dummy占住了头部的位置 curr可以随便走(因为单项链表不能返回)后面不用他了
    return dummy.next;
    //curr在最开始我们使用的是空节点,所以最后返回的应该是dummy.next 也就是空节点后面的第一个节点
}

9、两两交换链表中的节点

题目描述:给定一个链表 两两交换其中相邻的节点 并返回交换后的链表
解题思路:
一共需要六步 需要三个指针 交换n1和n2需要在n1n2前面设置一个p指针

1、n1 = p.next
2、 n2 = p.next.next
3、p.next = n2 从第三步开始操作
4、n1.next = n2.next
5、 n2.next = n1
6、 p = n1
原图:


在走第二遍:因为上一步p和n1交换了 可以直接一直持续 直到结束

开头处理:第一种方法使用dummy去处理(空头) 第二种用if单独去处理
return的时候使用dummy.next
代码实现:

var swaPairs = function(head){
    //创建一个dummy空节点在最开始
    let dummy = new ListNode();
    dummy.next = head ;
    //遍历链表定义的指针 这里的current其实就是上面说的p
    let current = dummy;
    //保证n1和n2存在 单个节点不需要交换
    while(current.next !== null && current.next.next !== null){
        //开始六个步骤
        let n1 = current.next
        let n2 = current.next.next;
        current.next = n2;
        n1.next = n2.next;
        n2.next = n1;
        current = n1;
    }
    return dummy.next;
    //最后为啥使用dummy.next前面说过
}

10、字符异位词分组

解释 两个单词 打乱顺序之后还能组成新的单词

思路:
两种方法:第一种是把数组进行排序 然后比较两个数组单词是否相同 最优解第二种是 空间换时间,使用26位数组
1、检查是否为空数组
2、简历一个长度为26的数组 起始值为0 26个0
3、遍历所欲字符串 将字母出现的频率放到数组中对应的位置上 利用ascii码
4遍历数组 按照相同字母出现频率进行分组归类(使用hashMap)
5、遍历map 将结果返回

代码实现:


var groupAnagrams = function (strs) {
    //检查数组是否为空
    if (strs.length === 0) {
        return [];
    }
    const map = new Map();
    //遍历字符串数组 遍历两次 遍历元素 或者元素出现多少次
    for (const str of strs) {
        const characters = Array(26).fill(0); //在长度为26的数组里面填充fill 值为0的数组
        for (let i = 0; i < str.length; i++) {
            const ascii = str.charCodeAt(i) - 97; //取到ascii码
            characters[ascii]++;
        }
        const key = characters.jion(","); //生成key并转化成字符串
        // 分类操作
        if (map.has(key)) {
            //把key放到新的map里面
            // map.set(key,map.get(key).push(str)) //这是es5的方法
            map.set(key, [...map.get(key), str]) //es6扩展方法
        } else {
            // 没有key的话就把key放到里面
            map.set(key, [str])
        }
    }
    //遍历map并返回结果
    const result = [];
    for(const arr of map){
        result.push(arr[1])
    }
    return result;

}

11、最大子序和(动态规划)

题目描述:给定一个整数数组nums 找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和

思路: 有两种 动态规划和分治法,这里采用动态规划
这题还思路还有点不是很明确后期补上

代码实现:

//最大子序和
 var maxSubArray = function(nums){
     const memo = [];
     memo[0] = nums[0];
     for(let i=1; i<nums.length;i++){
         memo[i]= Math.max(nums[i]+memo[i-1],nums[i]);
     }
     for(let i= 1; i<memo.length;i++){
         max = Math.max(max, memo[i]);
     }
     return max;

 }

12、螺旋矩阵


思路:把二维数组走完 需要几个变量 记录走的方向

1、如果数组为空 返回空数组
2.定义4个边界以及当前方向
3、当左边界小于等于右边界,且上边界小于等于下边界的时候,执行while循环 按照右下左上的顺序 依次将路径上的字符添加到结果里
4、while循环结束后 返回结果

代码实现:


//螺旋矩阵
 var spiralOrder = function(matrix){
     if(matrix.length === 0){
         return [];
     }
     //定义四边界以及当前的方向 这四个都比较好理解
     let top = 0;
     let bottom = matrix.length -1;
     let left = 0;
     let right = matrix[0].length -1;

     let direction = "right";//一开始是right
     let以上是关于力扣刷题算法笔记(javascript版)的主要内容,如果未能解决你的问题,请参考以下文章

力扣刷题资源

力扣刷题笔记由简到难,模块突破, 你与AC只差一句提示

力扣刷题笔记 1《二分查找》

持续更新力扣刷题笔记

持续更新力扣刷题笔记

力扣刷题之贪心算法(C++)