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

Posted 十九万里

tags:

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

上篇链接:
力扣刷题算法笔记(javascript版)上

视频链接:
人人都能看得懂的Leetcode力扣刷题教程合集

在笔记上中 一共学习了31道算法题,剩下大概20到算法题在花两天的学习和总结一下

1、 打家劫舍

题目描述:
你是一个专业的小偷 计划投钱沿街的房屋,每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通防盗系统 (所以不能偷相邻的两家)
给定一个代表每个房屋能存放金额的非负整数数组 计算你在不触动警报装置下 能偷到的最高金额。

不能偷相邻的房子 动态规划 记录到当前这个房子能收到的最大数
思路就是设定一值用来保存 然后动态的去比较相加的代码

//   33  打家劫舍
var rob = function(nums){
// 数组长度为0和1 的情况
    if(nums.length == 0){
        return 0;
    }
    if(nums.length === 1){
        return nums[0];
    }
    const memo = [];
    memo [0] = nums[0];
    memo[1] = Math.max(nums[0],nums[1]);
    for(let i = 2; i<nums.length;i++){
        //判断一下是选择在当前房子还是保留i-1最大的数
        memo[i] = Math.max(nums[i] + memo[i-2],memo[i-1]) 
    }
    return memo[nums.length -1] ;//返回最后一个数
}

// 优化  在选择偷哪一个数组的时候可以优化 不需要单独的变量 
//直接是用两个需要定义的变量

var rob = function(nums){
    // 数组长度为0和1 的情况
        if(nums.length == 0){
            return 0;
        }
        if(nums.length === 1){
            return nums[0];
        }
        
        let prev2 = nums[0];
        let prev1 = Math.max(nums[0],nums[1]);
        for(let i = 2; i<nums.length;i++){
            //判断一下是选择在当前房子还是保留i-1最大的数
            const temp = Math.max(nums[i] + memo[i-2],memo[i-1]) ;
            prev2 = prev1;
            prev1 = temp;
        }
        return prev1;
    }

2、岛屿数量

题目描述:给定一个有‘1(陆地)和0(水)组成的二维网络,
并且他是通过水平方向活垂直方向上相邻的陆地连接而成的,可以假设网格的四个边均被水包围了


分析思路:从最开始遍历访问 如果是1就记录下来 记作一个小岛,然后他周围是1的部分都是一个小岛 这里用到“沉没”就是把1全部变成0 这样接着往后面遍历 就不存在重复的问题了,最后就能数出小岛的数量
那么重点在于沉没这个过程:
把当前的点从1变为0 然后从这个点向周围(行和列)扩散 判断是否越界(越界取消)是0的话就不做操作· 判断是1就变为0 然后在这个基础上继续扩散检查 和之前的步骤一样;最后返回小岛的数量

bfs:广度优先搜索:
dfs:深度优先搜索:
代码实现:

    //判断岛屿数量
var numIslands = function (grid){
    let count = 0;//初始化小岛的数量为0
    function dfs(row,col){
        //判断越界和等于0的时候  不需要去处理
        if(row<0 || row >= grid.length || col<0 || col>=grid[0].length || grid[row][col] === "0"){
            return;
        }
      
        //如果不是1 则变为0 然后的调用  这里采用了递归的方法
        grid[row][col] = "0";
        dfs(row-1,col);
        dfs(row+1,col);
        dfs(row, col-1);
        dfs(row ,col+1);
    }
    //遍历二维数组:
    for(let row = 0;row<grid.length;row++){
        for(let col=0;col<grid[0].length; col++){
            if(grid[row][col] === "1"){//这里使用字符串1
                count++;//增加小岛数量
                dfs(row,col);//sink就是沉没函数 dfs

            }
        }
    }
    return count;
}

3、反转链表

链表的题 主要是玩指针

解题思路:
一、利用栈 (先进后出)的特点 然后在一个一个取出来 会多开一个数组
二、分四部 需要三个指针 prev curr next
初始:prev指向第一个节点前的空节点 curr指向第一个节点 next也指向第一个节点
第一步 next指向curr后一个节点 也就是第二个
第二步:curr的next指向prev (就是1指向0)
第三步:prev像后挪一步 prev= curr
第四步: curr指向next
这样就完成了一次反转 循环遍历到最后 当curr为空的时候 最后返回的是prev(因为已经反转了)

//反转链表 传统写法
var reverseList = function (head){
    let prev = null;
    let curr = head ;
    let next = null;
    while(curr !== null){
        next = curr.next;
        curr.next = prev; //指向反转
        prev = curr;
        curr = next;
    }
    return prev;
}
//ES6中解构赋值 可以直接使用

4、存在重复元素

描述:给定一个数组 判断是否存在重复元素 如果任何值在数组中都至少出现两次 函数返回true 如果数组中每个元素都不相同 则返回false

自己的解题思路:1、遍历 放入新数组比较/2、使用两个set 3.查找每个数组中出现数字的次数
利用数据结构 set中has属性 如果所有元素都返回has的话就最后返回true 否则返回false

// 存在重复元素
var containDuplicate = function (nums){
    const set = new Set();
    for(let i = 0;i<nums.length; i++){
        if(set.has(nums[i])){
            return true;
        }
        set.add(nums[i]);
    }
    return false;
}

5、存在重复元素2

描述:给定一个整数数组和一个整数k 判断数组中是否存在两个不同的索引i和j 是的nums[i]=nums[j[ 并且i和j的差的绝对值最大为k
判断在k个元素内有没有重复的元素

解题思路:使用map 遍历数组每个数 然后存到map里面 map的key设置为数组的值 value为值的索引,判断这个数在map中有没有 没有就把值和index放进去。 后续放进去重复的数字的时候 需要判断他们的索引值 相减的绝对值是否小于等于k 是的话就返回true 不是的话就继续 但是需要把当前重复的更新到后面重复这个值

代码实现:

// 存在重复元素
var containsNearDuplicate = function(nums,k){
    const map = new Map();//新建一个map用来存放数组 (主要用到index值)
    for(let i= 0;i<nums.length;i++){
    //如果没有i或者两个重复的i值小于k 返回true
        if(map.has(nums[i]) && (i-map.get(nums[i])  <=k)){
            return true;
        }else{//没有的话就正常插入
            map.set(nums[i],i);
        }
    }
    return false;
}

6、除自身以外数组的乘积

给定长度为n的整数数组nums,其中n>1 返回输出数组output、, 其中output【i】等于nums中除nums【i】之外其余各元素的乘积,

注:不能使用除法 且在0(n)时间复杂度内完成此题,
解题思路:如果没有注释 思路我觉得是全部乘积起来 然后除以nums[i]
i左边的相程 右边的乘积乘起来 然后在相乘 可以使用动态规划
需要两个表格 一个是从左到右的乘积 林外一个是从右到左的乘积

优化:使用一个变量 不断更新 使用5的值的时候那个变量乘于4的值

代码实现:

var productExcptSelf = function (nums){
    const result = Array(nums.length).fill(1);
    let product = 1;
    for(let i = 0; i<nums.length;i++){
        result[i] = result[i] * product;
        product = product * numns[i];
    }
    for(let i = nums.length-1; i>=0;i--){
        result[i] = result[i] * product;
        product = product * numns[i];
    }
    return result;
};

7 vaild Anagram

Anagram 的意思就是 一个单词可以通过调换顺序得到另一个单词 第一个单词里面每一个词出现的次数和第二个出现的次数一样
给定两个数组 判断这两个数组哈斯不会Anagram
Palindrome:单词从前面往后面的顺序和从后面往前面的顺序一样的

解题思路:
1、判断两个数组长度是否一直 不一致返回false
2、创建一个map 用来存储每个字符出现的次数
3、对于第一个单词的每个字母 也就是S1[i] 在map里将出现次数+1。对于第二个单词的每个字母 也就是s2[i] 在map里将出现次数-1,
4、遍历完成之后 检查map里的每一个字母出现次数是否是0 如果有一个非0的字母 则返回false 否则返回true;
代码实现:

//Anagram
 var isAnagram = function(s,t){
     //判断两个数组是否一致
     if(s.length !== t.length){
         return false;
     }
     const map = new Map();
    //  用一个for循环遍历两个数组
     for(let i=0;i<s.length;i++){
         if(map.has(s[i])){
            //  如果map有s[i] value就需要加一
             map.set(s[i],map.get(s[i])+1);
         }else{
             map.set(s[i],1);
         }
         if(map.has(t[i])){
            //  如果t有t[i]的话 value需要减一
            map.set(t[i],map.get(t[i]) - 1);
        }else{//没有就初始化为-1
            map.set(t[i],-1);
        }
     }
     //遍历map的时候使用map.forEach返回的是一个函数 不能返回外面的函数 
    //  需要使用for of 需要是哟const
     for (const letter of map){
         //letter的第二个如果不是0 的话 
        if(letter[1] !== 0){
            return false;
        }
     }
     return true;
 }

8、移动零

题目描述:给定一个数组nums 编写一个函数将所有的0移动到数组的末尾 同时保持非零元素的相对顺序(之前的顺序)

注:
1、必须在原数组上进行操作,不能拷贝额外的数组
2、尽量减少操作次数
解题思路:
不是一个一个换 把非零的数进行排序 剩余的位置填充0
需要两个指针:i j
先移动i i++ 如果i遇到的是0 则跳过继续下一个 如果不是0,把当前i的位置赋值给j 然后j++
当i移动到最后的时候 就把j后面的数都变成0
i移动的时候 o跳过数值交换 这样确保了相对的顺序 最后在进行0的填充 这样0就变到了最后面。
代码实现:

//  移动0
var moveZeroes = function (nums){
    let j = 0;
    for(let i= 0;i<nums.length; i++){
        if(nums[i] !== 0){
            nums[j] = nums[i];
        j++;        }
    }
    //上面循环完一遍之后把j后面的数字全部变化成0
    for (i=j;i<nums.length; i++){
        nums[i] = 0;
    }
    return nums;
}

9 奇偶链表

描述:给定一个单链表 吧所有的奇数节点和偶数节点分别排在一起 奇数和偶数值指的是index位置的奇偶数 不是数值的奇偶 奇数排到前面 偶数排到后面

请尝试用原地算法完成 空间复杂度为0(1)
解题思路: 需要两个指针 第一个在第一位(第一个奇数结点)第二个指针

1指向3 2指向4 然后循环这个过程 最后把7指向2
代码实现:

// 奇偶链表
var oddEvenList = function (head){
    //判断两种特殊情况
    if(head === null){
        return null;
    }
    if(head.next === null){
        return head;
    }
    //定义两个指针
    let  odd =head //奇数
    let even = head.next;
    let evenHead = head.next; //把偶数的头部占住 后面要连接奇数的尾部
    //判断是否循环结束作为条件
    while(even !== null && even.next !== null){
        //这里为什么不能直接使用3次next呢 
        odd.next = odd.next.next;
        odd = odd.next;
        even.next =even.next.next;
        even = even.next;
    }
    odd.next= evenHead;//把奇数尾部指向偶数头部
    return head;
}

10、两个数组的交集

描述:给定两个数组 判断他们的交集

注:输出结果中每个数都是唯一的 不考虑输出数组的顺序
思路解析:
个人:使用map 两个数组往里面插入 value大于1的就是交集
不能重复 就使用set
循环判断数组里面是否有相同的数
代码实现:

// 两个数组的交集
var intersection = function(nums1,nums2){
    const result = new Set();
   
    for(nums2.includes(nums)){
        result.add(num);
    }
}
return Array.from(result);

// 代码优化
 //数组搜索值 复杂度0(n) set Map -.has 复杂度0(1)
 // 两个数组的交集
var intersection = function (nums1, nums2) {
    const result = new Set();
    const set = new Set();
    for (num of nums2) {

        if (set.has(nums)) {
            result.add(num);
        }
    }

}
return Array.from(result);

11 甲板上的战舰

给定一个二维的甲板 请计算其中有多少艘战舰 战舰使用x表示 空位用“。”表示 需要遵守以下规则
1给你一个有效的甲板 仅有战舰或者空位组成
2、战舰只能水平或者垂直放置 也就是说只能由1xN(1行,n列)组成 或者Nx1(N行 1列)组成 其中N可以是任意大小
3、两艘战舰之间至少有一个水平或垂直的空位分隔,即没有相邻的战舰。


解题思路:
使用沉没 小岛个数问题 判断是x之后就记录 然后沉没 防止重复

// 甲板上的战舰
var countBattleships = function (board){
    let result = 0;
    //循环二维数组
    for (let row=0;row<board.length;row++){
        for(let col = 0;col<board[0].length;col++){
            //判断如果等于x 周围的也等于x 就继续  否则跳出循环
            if(board[row][col] === "X"){
                if(row>0&& board[row-1][col] === "X"){
                    continue;
                }
                if(col>0 && board[row][col-1] === "X"){
                    continue;
                }
            }
            result ++;
        }
    }
}

12 两数相加 ii

给定两个非空链表来代表两个非负整数,数字最高位位于链表的开始位置 他们的每个节点只存储一位数 除了数字0之外不会有0开头的

这道题要注意的是链表的长度可能不一致 所以我们不能直接从头开始相加 应该从尾部相加
解题思路:
先反转 在相加 然后再反转
利用栈 现金后出的特点 使用两个栈 依次压入 全部压入之后再取出
代码实现:

// 两链表相加
var addTowNumbers = function (l1,l2){
    const stack1 = [],stack2=[];
    while(l1 !==null){
        //判断不为1就往里面压入
        stack1.push(l1.val);
        l1=l1.next;
    }
    while(l2 !== null){
        stack2.push(l2.val);
        l2= l2.next;
    }
    let carry = 0,curr = null;
    while(stack.length !== 0 || stack2.length !== 0){
        let sum = 0;
        if(stack1.length !== 0){
            sum += stack1.pop();
        }
        //进位
        sum += carry;
        // 处理新链表中节点问题
        const node = newListNode(sum % 10)

以上是关于力扣刷题算法笔记(javascript版)下的主要内容,如果未能解决你的问题,请参考以下文章

力扣刷题资源

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

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

持续更新力扣刷题笔记

持续更新力扣刷题笔记

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