力扣刷题

469 阅读6分钟

前言

大概从三月初开始,踏入了力扣刷题的队伍。每天一道算法题准备春招,算下来好像也就二三十道(好少啊,突然觉得刷几百题的人太牛逼了)。过了一个月感觉大部分都忘了,就对自己以前刷过的题大概进行下总结

1. 303.区域和检索-数组不可变

1.1 题目描述

原题链接:leetcode-cn.com/problems/ra…

给定一个整数数组  nums,求出数组从索引 i 到 j(i ≤ j)范围内元素的总和,包含 i、j 两点。
实现 NumArray 类:

NumArray(int[] nums) 使用数组 nums 初始化对象
int sumRange(int i, int j) 返回数组 nums 从索引 i 到 j(i ≤ j)范围内元素的总和,包含 i、j 两点(也就是 sum(nums[i], nums[i + 1], ... , nums[j]))
示例:

输入:
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
输出:
[null, 1, -1, -3]

解释:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1)) 
numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))

1.2 思路分析

这个题目要计算第i个值到第j个值的总和。那我们可以先计算前n个数值的和的值,将值存到某个数组中(数组开头为0,这样方便计算第j个数到第0个数的值),计算i到j值总和时,直接相减就可以。比如:


sum=[0,0,0,0,0]
nums=[1,2,3,4]

sum[1]存储第1个数值之和 ,下标为0到0数值之和:
    sum[1] = sum[0] + nums[0] = 1
    
sum[2]存储第1个到第2个数值之和 , 下标为0到1数值之和:
    sum[2] = sum[1] + nums[1] = sum[0] + nums[0] + nums[1] = 0+1+2 = 3
    
sum[3]存储第1个到第3个数值之和 , 下标为0到2数值之和:
sum[3] = sum[2] + nums[2] = sum[0] + nums[0] + nums[1] + nums[2]= 0+1+2+3 = 6

····

取num下标为1到2的值
sum[3] - sum [1] 
    =( sum[0] + nums[0] + nums[1] + nums[2] ) - ( sum[0] + nums[0] )
    =nums[1] + nums[2]
    =6-1=5

1.3 代码

var NumArray = function(nums) {
    var a=nums.length
    sums = new Array(a+1).fill(0)
    for(let i=0;i<a;i++){
        sums[i+1]=sums[i]+nums[i]
    }
    console.log(sums)
};

NumArray.prototype.sumRange = function(i, j) {
    return sums[j+1]-sums[i]

};

2. 304.二维矩阵和检索-矩阵不可变

2.1 题目描述

原题链接:leetcode-cn.com/problems/ra…

给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,
右下角为 (row2, col2) 。
上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),该子矩形内元素的总和为 8。

示例:

给定 matrix = [  [3, 0, 1, 4, 2],
  [5, 6, 3, 2, 1],
  [1, 2, 0, 1, 5],
  [4, 1, 0, 1, 7],
  [1, 0, 3, 0, 5]
]

sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12

2.2 思路分析

该题想计算子矩形内元素总和,可跟上题解决方法一样,就是计算多行i到j的值相加,新建一个数组,存储对矩阵前缀相加之和,然后对每一行的子数组和计算总和。例如:

0 1 2                   0 0 1 3
3 4 5       ====>       0 3 7 12
3 4 2                   0 3 7 9

2.3 代码

var NumMatrix = function(matrix) {
    var i=matrix.length
    if(i>0){
        var j=matrix[0].length
        // sum =new Array[i,j+1]
        sum = new Array(i).fill(0).map(() => new Array(j + 1).fill(0));
        for (let a=0;a<i;a++){
            for(let b=0;b<j;b++){
                sum[a][b+1]=sum[a][b]+matrix[a][b]
            }
        }
    }
};

NumMatrix.prototype.sumRegion = function(row1, col1, row2, col2) {
    let sums=0
    for(row1;row1<=row2;row1++){
        sums=sum[row1][col2+1]-sum[row1][col1]+sums
    }
    return sums

};

3. 338.比特位计数

3.1 题目描述

原题链接:leetcode-cn.com/problems/co…

给定一个非负整数 num。对于 0inum 范围中的每个数字 i,
计算其二进制数中的 1 的数目并将它们作为数组返回。

示例 1:
输入: 2
输出: [0,1,1]

示例 2:
输入: 5
输出: [0,1,1,2,1,2]

3.2 思路分析

先用for循环遍历输出一遍,将每个数值转换成二进制,使用正则表达式计算搜索并返回数组,计算数组长度

3.3 代码

var countBits = function(num) {
    a=new Array()
    for(let i=0;i<=num;i++){
        a.push((i.toString(2).match(/1/g) ||[]).length)
    }
    return a;
};

4. 354.俄罗斯套娃信封问题

4.1 题目描述

原题链接:leetcode-cn.com/problems/ru…

给你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。
当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算 最多能有多少个 信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
注意:不允许旋转信封。

示例 1:

输入:envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出:3
解释:最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。
示例 2:

输入:envelopes = [[1,1],[1,1],[1,1]]
输出:1

4.2 思路分析

我们可以将信封数组按照大小排序,按照长度从小向大排序,如果长度相等,则按照宽度从大到小排序。定义一个数组f,每个数组对应的值记录每个信封所能包含最大的信封个数,之后对遍历数组将其和以前的信封最大值进行比较,如果 i 宽度大于数组 j ( i之前的信封 ) 的宽度,表示能套进信封内,则将f[i]的值改变,i能套住j,表示i能套住j能套住所有信封的值,取最大值。

4.3 代码


var maxEnvelopes = function(envelopes) {
    if (envelopes.length === 0) {
        return 0;
    }
    
    const n = envelopes.length;
    envelopes.sort((e1, e2) => {
        if (e1[0] !== e2[0]) {
            return e1[0] - e2[0];
        } else {
            return e2[1] - e1[1];
        }
    })
    console.log(envelopes)

    const f = new Array(n).fill(1);
    let ans = 1;
    for (let i = 1; i < n; ++i) {
        for (let j = 0; j < i; ++j) {
            if (envelopes[j][1] < envelopes[i][1]) {
                f[i] = Math.max(f[i], f[j] + 1);
            }
        }
        ans = Math.max(ans, f[i]);
    }
    return ans;
};

5. 503.下一个更大元素

5.1 题目描述

原题链接:leetcode-cn.com/problems/ne…

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),
输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,
这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。
如果不存在,则输出 -1。

示例 1:
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数; 
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

5.2 思路分析

要对数组循环搜索最大值,我们可以直接将数组后面再增添一个原数值的数组 这样可以不用循环遍历数组,对其进行比较

5.3 代码

var nextGreaterElements = function(nums) { 
    let arr = nums.concat(nums);
  
    return nums.map((item, id) => {
       
        for(let i = id + 1; i < arr.length; i++){
           
            if(item < arr[i]) return arr[i];
        }
        return -1;
    })
};

6. 224. 基本计算器

6.1 题目描述

原题链接:leetcode-cn.com/problems/ba…

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

示例 1:

输入:s = "1 + 1"
输出:2
示例 2:

输入:s = " 2-1 + 2 "
输出:3
示例 3:

输入:s = "(1+(4+5+2)-3)+(6+8)"
输出:23

提示:
1 <= s.length <= 3 * 105
s 由数字、'+'、'-'、'('、')'、和 ' ' 组成
s 表示一个有效的表达式

6.2 思路分析

提示里表明s 由数字、'+'、'-'、'('、')'、和 ' ' 组成,如果是+,括号无作用则对原数值无改变,比如1+(1+2)=1+1+2。如果为-,则括号的作用就是符号反转, 比如1-(2-3)=1-2+3。则只需要定义一个sign表示符号变化,stack记录符号变化。

6.3 代码

var calculate = function(s) {
    const stack = [1]
    let sign = 1;

    let sum =0
    const n =s.length;
    let i =0;
    while( i < n){
        if(s[i]===' '){
            i++
        }else if(s[i] === '+'){
            sign = stack[stack.length-1]
            i++
        }else if(s[i] ==='-'){
            sign = -stack[stack.length-1]
            i++
        }else if(s[i] ==='('){
            stack.push(sign);
            i++
        }else if(s[i] ===')'){
            stack.pop(sign)
            i++
        }else {
            let num=0
        while(i<n && !(isNaN(Number(s[i]))) && s[i] !==' '){
            num = s[i].charCodeAt() - '0'.charCodeAt();
            i++
            }
            sum+=sign*num
        }
    }
    return sum
};

7. 227.基本计算器2

7.1 题目描述

原题链接:leetcode-cn.com/problems/ba…

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。

示例 1:

输入:s = "3+2*2"
输出:7
示例 2:

输入:s = " 3/2 "
输出:1
示例 3:

输入:s = " 3+5 / 2 "
输出:5
 
提示:

1 <= s.length <= 3 * 105
s 由整数和算符 ('+', '-', '*', '/') 组成,中间由一些空格隔开
s 表示一个 有效表达式
表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
题目数据保证答案是一个 32-bit 整数

7.2 思路分析

该题需要先进行乘除,后加减。我们可以定义一个栈如果为加减就先把该数值入栈,为乘除我们可以将进栈最后一个元素输出与其进行运算 ,将运算后的值存入栈。

7.3 代码


var calculate = function(s) {
    //trim() 方法用于删除字符串的头尾空白符,
    s = s.trim()
    const stack = new Array()
    let a = '+'
    let num = 0
    const n = s.length

    for(let i =0 ; i<n ; i++){
        if(!isNaN(Number(s[i])) && s[i]!==' '){
            num =num*10+ s[i].charCodeAt() - '0'.charCodeAt()
        }
        if(isNaN(Number(s[i])) || i === n-1){
            switch(a){
                case '+':
                    stack.push(num)
                    break
                case '-':
                    stack.push(-num);
                    break
                case '*':
                    stack.push(stack.pop()*num)
                    break
                case '/':
                    stack.push(stack.pop() / num | 0 )
                    break
            }
            a = s[i]
            num = 0;
        }
    }
    let ans = 0
    while(stack.length){
        ans +=stack.pop()
    }
   return ans;
};

8. 456.132模式

8.1 题目描述

原题链接:leetcode-cn.com/problems/13…

给你一个整数数组 nums ,数组中共有 n 个整数。132 模式的子序列 由三个整数 nums[i]
nums[j] 和 nums[k] 组成,并同时满足:i < j < k 和 nums[i] < nums[k] < nums[j] 。
如果 nums 中存在 132 模式的子序列 ,返回 true ;否则,返回 false 。

示例 1:
输入:nums = [1,2,3,4]
输出:false
解释:序列中不存在 132 模式的子序列。

示例 2:
输入:nums = [3,1,4,2]
输出:true
解释:序列中有 1 个 132 模式的子序列: [1, 4, 2] 。
示例 3:

输入:nums = [-1,3,2,0]
输出:true
解释:序列中有 3 个 132 模式的的子序列:[-1, 3, 2][-1, 3, 0][-1, 2, 0] 。

提示:
n == nums.length
1 <= n <= 104
-109 <= nums[i] <= 109


8.2 思路分析

我们可以从后往前寻找,假设最后一个数为a,往前寻找是否有比最后一个数值大的数b。如果有那么a为第二大的数,b为最大数,再往前寻找是否有比b还大的数值,如果有那b为第二大,把值赋给a,只要有个数小于a就为132模式。如果没有值大于a,则将其数值入栈(nums为...456模式),栈尾为最小值,再往前搜寻,如果大于栈尾的值,原理和1一样

8.3 代码

var find132pattern = function(nums) {
    var len = nums.length
    var max = -Number.MAX_SAFE_INTEGER
    var  stack =[nums[len-1]]

    for(let i = len-2;i>=0;i--){
        if(nums[i]<max){
            return true
        }
        while(stack.length  && nums[i] > stack[stack.length-1]){
            max = stack[stack.length-1]
            stack.pop()
        }
        if(nums[i]>max){
            stack.push(nums[i])
        }
    }
    return false

9. 扁平化嵌套列表迭代器

9.1 题目描述

原题链接:leetcode-cn.com/problems/fl…

给你一个嵌套的整型列表。请你设计一个迭代器,使其能够遍历这个整型列表中的所有整数。
列表中的每一项或者为一个整数,或者是另一个列表。其中列表的元素也可能是整数或是其他列表。

示例 1:
输入: [[1,1],2,[1,1]]
输出: [1,1,2,1,1]
解释: 通过重复调用 next 直到 hasNext 返回 falsenext 返回的元素的顺序应该是: [1,1,2,1,1]。

示例 2:
输入: [1,[4,[6]]]
输出: [1,4,6]
解释: 通过重复调用 next 直到 hasNext 返回 falsenext 返回的元素的顺序应该是: [1,4,6]。

9.2 思路分析

我们可以使用嵌套函数,遍历列表,如果列表中的数值为数字,建一个栈接收这个值,否则为列表,则其传给函数重新遍历

9.3 代码

var NestedIterator = function(nestedList) {
    arrs =[];
    const dfs = (nestedList) =>{
        for(const nest of nestedList){
            if(nest.isInteger()){
                arrs.push(nest.getInteger())
            }else{
                dfs(nest.getList())
            }
        }
    }
    dfs(nestedList)

};

NestedIterator.prototype.hasNext = function() {
    return arrs.length>0
    
};
NestedIterator.prototype.next = function() {
    
    const arr= arrs[0];
    // console.log(arrs,arr)
    arrs = arrs.slice(1);
    return arr;
};

10. 两数相加

10.1 题目描述

原题链接:leetcode-cn.com/problems/ad…

给你两个 非空 的链表,表示两个非负的整数。
它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
 
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

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

10.2 思路分析

我们同时遍历两个链表,逐位计算它们的和,并与当前位置的进位值相加。具体而言,如果当前两个链表处相应位置的数字为 n1 、 n2进位值为carry,则它们的和为 n1+n2+carry。如果两个链表的长度不同,则可以认为长度短的链表的后面有若干个 00 。此外,如果链表遍历结束后,carry>0,还需要在答案链表的后面附加一个节点,节点的值为 carry。用head来记录加的数值。

10.3 代码

var addTwoNumbers = function(l1, l2) {
    let head = null, tail = null;
    let carry = 0;
    while (l1 || l2) {
        const n1 = l1 ? l1.val : 0;
        //val() 方法返回或设置被选元素的值。
        const n2 = l2 ? l2.val : 0;
        const sum = n1 + n2 + carry;
        if (!head) {
            head = tail = new ListNode(sum % 10);
            // console.log(head)
        } else {
            tail.next = new ListNode(sum % 10);
            // console.log(tail,tail.next)
            tail = tail.next;
        }
        // console.log(tail)
        carry = Math.floor(sum / 10);
        //floor() 方法可对一个数进行下舍入
        if (l1) {
            l1 = l1.next;
        }
        if (l2) {
            l2 = l2.next;
        }
    }
    if (carry > 0) {
        tail.next = new ListNode(carry);
    }
    return head;
};

写不动了,下次再说......