leetcode hot100解题总结 JS(持续更新)

Posted 九是我呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode hot100解题总结 JS(持续更新)相关的知识,希望对你有一定的参考价值。

53. 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let res = nums[0], curSum = nums[0];
    for(let i= 1; i< nums.length; i++){
        curSum = Math.max(nums[i], curSum + nums[i])
        res = Math.max(res, curSum)
    }
    return res
};

121. 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
思路:遍历一遍,将最小值记录下来,然后对比当前值和最小值的差值

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    let min = prices[0]  //min为最小值
    let res = 0  //res为差值
    for(let i =1; i<prices.length; i++){
        min = Math.min(min, prices[i])  //比较最小值和当前值,选最小
        res = Math.max(res, prices[i]- min) // 比较最小差值和当前差值
    }
    return res
};

141. 环形链表
给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
    let p = q = head
    if(p == null){ //判断头是否为空,不然后面q.next会执行出错
        return false
    }
    while(q.next!=null && q.next.next !=null){ 
        p = p.next;
        q = q.next.next
        if(q == p && q!=null && p !=null){
            return true
        }
    }
    return false
};

101对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
分析:这道题难点在于递归,是我已知以来的难点,很多时候明明知道用递归,但还是会不知所措。

var isSymmetric = function(root) {
//判断根是否为空
    if(root==null){  
        return true;
    }
    return isTrue(root.left, root.right);
};

//递归判断是否为对称,很容易想到,要判断左子树和右子树的值是否一致,
//若一致,再判断其左子树的左值和右子树的右值是否一致,以及左子树的右值和右子树的左值是否一致,
//其实这个过程是在判断相应的子树是否对称。终止条件就是若左右其中一个为空,则假,若左右均为空,则真。
function isTrue(leftTree, rightTree){

    if(leftTree == null && rightTree ==null){
        return true;
    }
    if((!leftTree && rightTree) || (leftTree && !rightTree)){
        return false;
    }
    return (leftTree.val == rightTree.val&& isTrue(leftTree.right, rightTree.left) && isTrue(leftTree.left, rightTree.right))
}

21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
 //难点:总是碰到指针问题就不知所措
//两个不同长度的链表,判断到最后,如果一个为空 ,则将另外一个正便利的节点附给那个节点的next就行
var mergeTwoLists = function(l1, l2) {
    let p = l1;
    let q = l2;
    let head = new ListNode(-1);
    let prev = head;
    if(l1 == null){
        return l2;
    }else if(l2 ==null){
        return l1;
    }
    while(l1 && l2){ //判断是否一方已遍历完成
        if(l1.val <= l2.val){
            prev.next = l1; //用的原来的节点而不是新创节点
            l1 = l1.next;    
        }else if(l1.val >= l2.val){
            prev.next = l2;
            l2 = l2.next;          
        }
        prev = prev.next;
    }
    prev.next = l1 == null ? l2 : l1;  //如何让处理剩下的节点,像链表是连着的就可以直接确定一个就行,数组不同的地方就是需要一个个加进去
    return head.next;

};

11. 盛最多水的容器
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。说明:你不能倾斜容器。
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

/**
 * @param {number[]} height
 * @return {number}
 */
//双指针方法
var maxArea = function(height) {
    //解构语法进行赋值,若做指针值小于右指针,则计算体积,并将左指针右移,否则计算体积,并将左指针移动。为什么每次都将短的一方移动呢,因为V = 短板长度*t, t为两个值的间距,但是无论右指针左移还是左指针右移,下一个t为t-1,肯定会减小,所以我们只需关注新短板长度即可。如果移动长板的话,短板肯定不变或者较少,肯定不会变大。但是移动短板的话,可能会变大或者不变或者减少。

    let [maxV, i, j] =[0, 0, height.length -1];
    while(i < j){
        if (height[i] <= height[j]){
            maxV = Math.max(maxV , (j-i) * height[i]);
            i++;
        } else if(height[i] > height[j]){
            maxV = Math.max(maxV , (j-i) * height[j]);
            j--;
        }
    }
    return maxV;
};

5. 最长回文子串(2021.7.25)
给你一个字符串 s,找到 s 中最长的回文子串。

// 方法一:暴力解决
// 遍历每个子串,看是否是回文串
/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function(s) {
    let len = (s.length + 1) * s.length /2;
    let max =0;
    let map1 = new Map();
    for(let i =0; i< s.length; i++){
        for(let j =i; j<s.length;j++){
            if(isTruth(s.slice(i,j+1))){
                if(s.slice(i,j+1).length > max) {
                    map1.set(s.slice(i,j+1).length, s.slice(i,j+1));
                    max =  s.slice(i,j+1).length;
                }
            }
        }
    }
    return map1.get(max);

};

var isTruth = ( subStr) =>{
    for(let i = 0; i< Math.ceil(subStr.length/2); i++){
        if(subStr[i] != subStr[subStr.length - i -1]){
            return 0;
        }
    }
    return 1;
}

 //中心扩展法
 //思路:从左到右每次选一个数作为中心,向左右两边扩展,先找到相同的元素(先向右循环,直至不相等,再向左循环,直至不相等),然后再看左右两边的元素是否相等,如果相等,分别向左向右移动。不等则计算
 var longestPalindrome = function(s){
    let len;
    let max = 0;
    // 取最长长度时的maxStart,maxEnd,其实maxStart代表索引的前一位,maxEnd代表索引的后一位。
    let maxStart = maxEnd = 0;
    //遍历所有元素
    for(let cur = 0; cur <s.length; cur++){
        let right = left = cur;
        //先向右循环,此时right指向不相等的那个元素,right最大值为s.length,即右索引的后一个
        while(right <= s.length){
            if(s[right] != s[cur] ){
                break;
            }
            right ++;
        }
        //再向左循环,此时left指向不相等的那个元素,left最小为-1,即左索引的前一位
        while(left>= 0){
            if(s[left] != s[cur] ){
                break;
            }
            left--;
        }
        //分别向左向右移动
        while(left>= 0 && right <= s.length && s[right] == s[left]){
            left--;
            right++;
        }
        maxStart = len > max ? left : maxStart;
        maxEnd = len > max ? right : maxEnd;  
        max = len > max ? len : max;
    }
    return s.slice(maxStart+1, maxEnd);
 }

插播1(2021字节前端提前批,惨痛的教训)
415.给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。(2021.7.24)

/**
 * @param {string} num1
 * @param {string} num2
 * @return {string}
 */
 // 方法一
var addStrings = function(num1, num2) {
    let res = \'\';
    let len1 = num1.length;
    let len2 = num2.length;
    let i = 0, j = 0, temp = 0;
    // 填充0
    len1 < len2 ? num1 = num1.padStart(len2, \'0\') : num2 = num2.padStart(len1, \'0\');
    let newLen = len1 > len2 ? len1 :len2;
    for(let i = newLen - 1; i >= 0; i--){
        let sum = parseInt(num1[i]) + parseInt(num2[i]) + temp;
        temp = sum > 9 ? 1 : 0;  //可优化为temp = Math.floor(sum / 10)
        res =  (sum > 9 ? sum % 10 : sum)  + res; // res = sum % 10
    }
    if(temp === 1){
        res = \'1\' + res;
    }
    return res;
};

//方法二: 上面占据了很多空间,可以进行判断.但这种虽然时间快,但占内存
//执行用时:92 ms , 在所有 javascript 提交中击败了 68.05%的用户
//内存消耗: 39.9 MB , 在所有 JavaScript 提交中击败了 69.93%的用户
var addStrings = function(num1, num2) {
    let i = num1.length - 1, j = num2.length - 1, add = 0;
    const ans = [];
    while (i >= 0 || j >= 0 || add != 0) {
        const x = i >= 0 ? num1.charAt(i) - \'0\' : 0;
        const y = j >= 0 ? num2.charAt(j) - \'0\' : 0;
        const result = x + y + add;
        ans.push(result % 10);
        add = Math.floor(result / 10);
        i -= 1;
        j -= 1;
    }
    return ans.reverse().join(\'\');
};

4. 寻找两个正序数组的中位数(2021.7.22)

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */

var findMedianSortedArrays = function(nums1, nums2) {
  //整体思路:把这两个数组遍历按照大小顺序保存在一个新数组中,然后再更具两者长度是奇数还是偶数来输出结果
  let len1 = nums1.length;
  let len2 = nums2.length;
  let mid = (len1+len2) /2;
  let temp = 0;
  let i= 0,j=0;
  let arr =[];
  while(i<len1 || j<len2){
    if(nums1[i]<=nums2[j] || j>= len2 && i< len1){
      arr[temp] = nums1[i]
      i++;
    }else{
      arr[temp] = nums2[j]
      j++;
    }
    temp++;
    // if(temp>mid){
    //     break;
    // }
  }
  
  let res = ((len1+len2)%2 === 1) ? arr[Math.floor(arr.length / 2)] : (arr[arr.length / 2 - 1] + arr[arr.length / 2]) / 2
  return res;
};

试图优化,但失败,目前的复杂度为O(m+n),本来想判断一下temp如果大于一半的话,就提前break,但是不知道是不是js不允许arr[temp],所以解决不出来,虽然这个判断并不影响复杂度,因为官方可以给出O(log(m+n))的复杂度:可以用二分查找方法。

  1. 无重复字符的最长子串(2021.7.21)
    给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let map = new Map();
    let max = 0;
    let start = 0;
    let end = 0;
    while(end != s.length){
        if(!map.has(s[end])){
        } else {
            start = map.get(s[end]) > start ? map.get(s[end]) : start;
        }
        map.set(s[end], end+1);
        ans = end - start +1;
        end++;
        max = ans > max ? ans : max;
    }
    return max;
}

2.两数相加(2021.7.20)
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var addTwoNumbers = function(l1, l2) {
    let temp = 0;
    let head = null;
    let tail = null;
    while(l1 || l2){
        let a = l1 ? l1.val : 0;
        let b = l2 ? l2.val : 0;
        let sum = a + b + temp;
        if(!head){
            head = tail = new ListNode(sum%10, null);
        } else{
            tail.next = new ListNode(sum%10);
            tail = tail.next;
        }
        temp = sum > 9 ? 1 : 0;
        if(l1){
            l1 = l1.next;
        }
        if(l2){
            l2 = l2.next;
        }
    }
    if(temp>0){
        tail.next = new ListNode(temp);
    }
    return head;
};

1.两数之和(2021.7.20)
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let numsSet = new Set();
    for(let i=0;i<nums.length;i++){
        let temp = target-nums[i]
        if(numsSet.has(temp)){
            return [i,nums.indexOf(temp)]
        }
        numsSet.add(nums[i])
    }

};

以上是关于leetcode hot100解题总结 JS(持续更新)的主要内容,如果未能解决你的问题,请参考以下文章

leetcode热题Hot100——LRU缓存

LeetCode 热题 HOT 100

LeetCode Hot 100 --- 旋转数组(java)

LeetCode 热题 HOT 100

leetCode HOT100

LeetCode算法题 菜鸟总结 持续更新中