LeetCode基础算法题(js)

数组

2021.07.05

两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
首先拿到这道题,我们基本马上可以想到,此题可以看成是一道传统的映射题(map映射),为什么可以这样看呢,因为我们需找出两个数组的交集元素,同时应与两个数组中出现的次数一致。这样就导致了我们需要知道每个值出现的次数,所以映射关系就成了<元素,出现次数>。剩下的就是顺利成章的解题。

/**利用哈希思想,在js中用Set解决
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function(nums1, nums2) {
    let hash1 = new Set(nums1)
    let hash2 = new Set()
    for(let i = 0; i < nums2.length; i++) {
        if(hash1.has(nums2[i])){
            hash2.add(nums2[i])
        }
    }
    return [...hash2]
/*[...hash2],是ES6中的解构*/    
};

最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 “”
示例 1:
输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”
示例 2:
输入:strs = [“dog”,“racecar”,“car”]
输出:""
解释:输入不存在公共前缀。

/**
* @param {string[]} strs
* @return {string}
*/
var longestCommonPrefix = function(strs) {
    if(!strs){
        return""
    }
    var base=strs[0];
    for(var i=1;i<strs.length;){
        var word=strs[i];
        if(word.slice(0,base.length)==base){
            i++
        }else{
            base=base.substring(0,base.length-1)
        }
    }
   return base
};

买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(prices) {
   let minnum=Number.MAX_SAFE_INTEGER;
   let max=0;
   for(i=0;i<prices.length;i++){
       if(minnum>prices[i]){
           minnum=prices[i];
       }
        max=Math.max(max,prices[i]-minnum)
   }
   return max;
};

2021.07.06

旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]

示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]

方法一:
var rotate = function(nums, k) {
/*splice():该方法向或者从数组中添加或者删除项目,返回被删除的项目。(该方法会改变原数组)

slice(start,end):方法可从已有数组中返回选定的元素,返回一个新数组,包含从start到end(不包含该元素)的数组元素。
注意:该方法不会改变原数组,而是返回一个子数组,如果想删除数组中的一段元素,应该使用Array.splice()方法。
*/
  nums.push(...nums.splice(0, nums.length - k % nums.length))
};

方法二:
三次翻转,先翻转整个数组,再把两段翻转

删除有序数组重复项

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例 2:
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

第一种(非最优解)
var removeDuplicates = function(nums) {
let set = new Set(nums);
let arr= [...set];
    for(let i =0 ;i < arr.length;i++){
        nums[i]=arr[i];
    }
    return arr.length;
}
第二种
var removeDuplicates = function(nums) {
    let len = 1;
    for(let i =1 ; i < nums.length; ){
        if(nums[i] == nums[i-1] ){
            i++;
        }else{
            nums[len++] = nums[i++];
        }
    }
    return len;
};

加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:
输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。

示例 2:
输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。

示例 3:
输入:digits = [0]
输出:[1]

/**
 * @param {number[]} digits
 * @return {number[]}
 注意:return及时终止循环,否则置0循环继续,0也属于不等于9的情况*/
var plusOne = function(digits) {
    for(i=digits.length-1;i>=0;i--){
        if(digits[i]!==9){
            digits[i]=digits[i]+1;
            return digits
        }else(
            digits[i]=0
        )
    }
    digits=[1,...digits];
    return digits
};

2021.07.07

两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]

/*解法一:暴力循环,两层for*/
class Solution {
    public int[] twoSum(int[] nums, int target) {
       int n = nums.length;
       for(int i = 0;i<n;++i){
           for(int j = i+1;j<n;++j){
               if (nums[i] + nums[j]==target){
                   return new int[]{i,j};
               }
           }
       }
       return new int[0];
    }
}
/*解法二:map,值唯一*/
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    var map=new Map();
    for(i=0;i<nums.length;i++){
        var cha = target - nums[i];
        if(map.has(cha) ){
            return[map.get(cha),i]
        }else{
            map.set(nums[i],i)
        }
    }
};

三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:
输入:nums = []
输出:[]

示例 3:
输入:nums = [0]
输出:[]

方法一:三层循环,暴力解法
方法二:
先排序,然后运用双指针,固定最左边元素,固定元素要小于零
然后固定left和right指针

在这里插入图片描述

Z字形变换

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:

L   C   I   R
E T O E S I I G
E   D   H   N

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。

示例 1:

输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
示例 2:
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I

小浩算法解题

链表

2021.07.08

链表翻转

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

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

示例 3:
输入:head = []
输出:[]

var reverseList = function(head) {
    var prev = null;
    var curr = head;
    while(curr){
        next=curr.next;
        curr.next=prev;
        prev=curr;
        curr=next;
    }
    /*返回的是表头,表头为prev*/
    return prev;
};

删除链表倒数第N个节点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:你能尝试使用一趟扫描实现吗?

示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:
输入:head = [1], n = 1
输出:[]

示例 3:
输入:head = [1,2], n = 1
输出:[1]

/** 快慢指针,双指针
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    var dumy = new ListNode(0,head);
    var fast = head;
    var slow = dumy;
    for(var i =0;i<n;i++){
        fast=fast.next;
    }
    while(fast!=null){
        fast=fast.next;
        slow=slow.next;
    }
    slow.next=slow.next.next;
    return dumy.next;
};

合并两个有序链表

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

示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

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

示例 3:
输入:l1 = [], l2 = [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 mergeTwoLists = function(l1, l2) {
    
    if (l1 === null) {
        return l2;
    } else if (l2 === null) {
        return l1;
    } else if (l1.val < l2.val) {
        l1.next = mergeTwoLists(l1.next, l2);
        return l1;
    } else {
        l2.next = mergeTwoLists(l1, l2.next);
        return l2;
    }
    }

2021.07.09

环形链表

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

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

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

//解题思路:设置快慢指针,若快指针可以追上慢指针,说明有环

var hasCycle = function(head) {
    if(head === null || head.next === null) {
    return false;
  }
  var slow=head;
  var fast=head.next;
  while(fast&&fast.next){
      if(fast==slow){
          return true
      }
      fast=fast.next.next;
      slow=slow.next;
  }
  return false
};

两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 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]

var addTwoNumbers = function(l1, l2) {
    let head = null, tail = null;
    let carry = 0;
    while (l1 || l2) {
        const n1 = l1 ? l1.val : 0;
        const n2 = l2 ? l2.val : 0;
        const sum = n1 + n2 + carry;
        if (!head) {
            head = tail = new ListNode(sum % 10);
        } else {
            tail.next = new ListNode(sum % 10);
            tail = tail.next;
        }
        carry = Math.floor(sum / 10);
        if (l1) {
            l1 = l1.next;
        }
        if (l2) {
            l2 = l2.next;
        }
    }
    if (carry > 0) {
        tail.next = new ListNode(carry);
    }
    return head;
};

二叉树

2021.07.11

最大深度与DFS

每个节点的深度与它左右子树的深度有关,且等于其左右子树最大深度值加上 1 。

示例:
给定二叉树 [3,9,20,null,null,15,7]3
  /  \
  9  20
    /  \
   15   7
返回它的最大深度 3
//递归法
var maxDepth = function(root) {
  if(root!=null){
        var left = maxDepth(root.left)
        var right = maxDepth(root.right)
        return left>right? left+1:right+1
    }
    return 0
}

//DFS深度优先遍历,先右进站,再左进站
var maxDepth = function(root) {
    let list =[];
    let stack =[root];
    while(stack.length!=0){
        let targrt = stack.pop();
        list.push(targrt.value)
        if(targrt.right!=null){
            stack.push(targrt.right)
        }
        if(targrt.left!=null){
            stack.push(targrt.left)
        }
    }
    return list

层次遍历与BFS

BFS广度/宽度优先

给定二叉树 [3,9,20,null,null,15,7]3   
   / \  
  9  20    
    /  \  
   15   7
返回其层次遍历结果:[[3],[9,20],[15,7]]
var levelOrder = function(root) {
    if (!root) return [];
    const ans = [];
    const queue = [root];
    while (queue.length) {
        const len  = queue.length;
        const arr = [];
        for (let i = 0; i < len; i++) {
        //shift是截取数组的第一个元素
            const node = queue.shift();
            arr.push(node.val);
            if (node.children) {
                for (let i = 0; i < node.children.length; i++) {
                    queue.push(node.children[i]);
                }
            }
        }
        ans.push(arr);
    }
    return ans;
};

二叉搜索树BST与其验证

二叉搜索树(Binary Search Tree),(又:二叉查找树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值,它的左、右子树也分别为二叉搜索树。

输入:
   5
  / \
 1   4
    / \
   3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4

平衡二叉树

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

完全二叉树

翻转二叉树

2021.07.12

var invertTree = function(root) {
    if(root==null){
        return null
    }
    const left=invertTree(root.left)
    const right = invertTree(root.right)
    root.left=right
    root.right=left
    return root
};

二分法

用二分查找最多需要log2n步,用于解决在递增递减区间中搜索目标值

阿珂喜欢吃香蕉

这里总共有 N 堆香蕉,第 i 堆中有piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。 阿珂可以决定她吃香蕉的速度 K (单位:根/小时),每个小时,她将会选择一堆香蕉,从中吃掉 K 根。
如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。
珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。

示例 1:
输入: piles = [3,6,7,11], H = 8
输出: 4

示例 2:
输入: piles = [30,11,23,4,20], H = 5
输出: 30

示例 3:
输入: piles = [30,11,23,4,20], H = 6
输出: 23

/**
 * @param {number[]} piles
 * @param {number} H
 * @return {number}
 */
var minEatingSpeed = function (piles, H) {
    // 找到最大值,确认速度区间
    let max = -Infinity;
    for (let i = 0; i < piles.length; i++) {
        max = Math.max(max, piles[i]);
    }

    // 左闭右开,[left, right)
    let left = 0;
    let right = max; 

    // 二分法收敛区间
    while (left < right) {
        const mid = Math.floor(left + (right - left) / 2);
        const midHour = costHour(piles, mid);
        if (midHour === H) {
            right = mid; // 收敛右侧边界
        } else if (midHour > H) {
            left = mid + 1;
        } else if (midHour < H) {
            right = mid;
        }
    }
    return right; // 循环终止条件为left===right, 所以这里返回left也是可以的
};

const costHour = (piles, k) => {
    let h = 0;
    for (let i = 0; i < piles.length; i++) {
        h += Math.ceil(piles[i] / k);
    }
    return h;
}

x的平方根

实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:
输入: 4
输出: 2

示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842…,
由于返回类型是整数,小数部分将被舍去。

var mySqrt = function(x) {
    if (x < 2) return x
    let left = 1
    let right = Math.floor(x / 2);
    while (left <= right) {
        const mid = Math.floor(left + (right - left) / 2)
        if (mid * mid === x) return mid
        if (mid * mid < x) {
            left = mid + 1
        }else {
            right = mid - 1
        }
    }
    return right
};
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值