LeetCode热题100(JavaScript)

哈希

两数之和

暴力解法

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    for(let i =0;i<nums.length;i++){
        let x1 = nums[i]
        for(let j = 0 ; j<nums.length;j++){
            if(i!=j){
                let x2 = nums[j]
                if(x1+x2==target){
                    return [i,j]
                }
            }
        }
    }
};

Map法

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

字母异味词分组

/**
 * @param {string[]} strs
 * @return {string[][]}
 */
var groupAnagrams = function(strs) {
    let map = new Map();  
  
    for (let i = 0; i < strs.length; i++) {  
        let word = strs[i];  
        // 使用数组的 sort 方法对字符串的字符进行排序,并将排序后的字符数组重新连接成字符串  
        let sorted_word = word.split('').sort().join('');  
  
        if (map.has(sorted_word)) {  
            map.get(sorted_word).push(word);  
        } else {  
            map.set(sorted_word, [word]);  
        }  
    }  
  
    // 将Map中的值(数组)转换为数组返回  
    return [...map.values()];  
};

最长连续序列

var longestConsecutive = function(nums) {
    if (nums.length === 0) return 0;  
  
    let set = new Set(nums);  
    let maxSize = 0;  
  
    for (let num of set) {  
        // 跳过非连续序列的起始数字(即那些前面有数字的数字)  
        if (!set.has(num - 1)) {  
            let currentNum = num;  
            let currentLength = 1;  
  
            // 遍历连续序列  
            while (set.has(currentNum + 1)) {  
                currentNum++;  
                currentLength++;  
            }  
  
            // 更新最大长度  
            maxSize = Math.max(maxSize, currentLength);  
        }  
    }  
  
    return maxSize; 
};

双指针

移动零

解法1

先遍历数组,计算非零元素的数量。然后,在第二次遍历中,只将非零元素复制回数组的前部,并跳过0。

function moveZeroes(nums) {  
    let count = 0;   
    for (let num of nums) {  
        if (num !== 0) {  
            count++;  
        }  
    }  
    let writeIndex = 0;   
    for (let num of nums) {  
        if (num !== 0) {  
            nums[writeIndex++] = num;  
        }   
    }  
    // 填充剩余的0
    for (let i = writeIndex; i < nums.length; i++) {  
        nums[i] = 0;  
    } 
    return nums
}  

解法2

  1. 初始化两个指针,slow 和 fast,都指向数组的第一个元素。
  2. 遍历数组(fast 从头到尾),对于每个 nums[fast]
    • 如果 nums[fast] 不为 0,则交换 nums[slow] 和 nums[fast],并将 slow 向后移动一位(slow++)。
    • 如果 nums[fast] 为 0,则只移动 fast 指针。
  3. 遍历结束后,所有非零元素都已经被移动到了数组的前面,而后面的元素则自动成为了 0
var moveZeroes = function(nums) {
    let slow=0
    let fast = 0
    for(fast;fast<nums.length;fast++){
        if(nums[fast]!=0){
            let swap = nums[slow]
            nums[slow] = nums[fast]
            nums[fast] = swap
            slow++
        }
    }
    return nums
};

盛最多水的容器

  1. 初始化左指针 left 为 0,右指针 right 为 height.length - 1
  2. 进入循环,当 left < right 时执行以下步骤:
    • 计算当前容器的面积 area = Math.min(height[left], height[right]) * (right - left)
    • 更新最大面积 maxArea(如果 area 大于 maxArea)。
    • 如果 height[left] < height[right],则 left++(移动左指针),否则 right--(移动右指针)。
  3. 退出循环后,maxArea 就是所求的最大面积。
var maxArea = function(height) {
    let left = 0
    let right = height.length-1
    let max = 0
    while(left<right){
        let h1 = height[left]
        let h2 = height[right]
        let area = Math.min(h1, h2) * (right - left)
        max = Math.max(max, area);  
        if (h1 > h2) {  
            right--;  
        } else {  
            left++;  
        } 
    }
    return max
};

三数之和

  1. 排序:首先对输入的数组nums进行排序。这是为了之后能够使用双指针技术有效地查找匹配项。

  2. 初始化:定义结果数组res来存储找到的三元组,以及定义指针ileftright分别用于遍历数组和作为双指针。

  3. 遍历数组:使用外层循环遍历数组nums,其中i是第一个数的索引。为了避免找到重复的三元组,如果当前元素与前一个元素相同,则跳过。

  4. 双指针查找:在内层循环中,使用leftright两个指针,在i之后的元素中查找满足nums[i] + nums[left] + nums[right] == 0的另外两个数。lefti+1开始,right从数组末尾开始。

  5. 计算和并调整指针:计算当前三个数的和sum。如果sum等于0,说明找到了一个满足条件的三元组,将其添加到结果数组中,并移动leftright指针,同时跳过所有相同的元素,以避免重复的三元组。如果sum小于0,说明和太小,需要增加,因此将left指针右移。如果sum大于0,说明和太大,需要减小,因此将right指针左移。

  6. 返回结果:当遍历完整个数组后,所有满足条件的三元组都已被添加到结果数组中,返回结果数组。

var threeSum = function(nums) {
    // 首先对数组进行排序  
    nums.sort((a, b) => a - b);  
    const result = [];  
    let n = nums.length;  
  
    // 遍历数组(除了最后两个元素,因为需要至少两个元素与当前元素相加)  
    for (let i = 0; i < n - 2; i++) {  
        // 跳过所有与当前nums[i]相同的元素,以避免重复的三元组  
        if (i > 0 && nums[i] === nums[i - 1]) continue;  
  
        let left = i + 1;  
        let right = n - 1;  
  
        // 使用双指针在nums[i]之后的元素中查找  
        while (left < right) {  
            const sum = nums[i] + nums[left] + nums[right];  
  
            if (sum === 0) {  
                // 找到一个和为0的三元组,添加到结果中  
                result.push([nums[i], nums[left], nums[right]]);  
  
                // 移动左右指针,并跳过所有相同的元素  
                while (left < right && nums[left] === nums[left + 1]) left++;  
                while (left < right && nums[right] === nums[right - 1]) right--;  
  
                left++;  
                right--;  
            } else if (sum < 0) {  
                // 和小于0,说明需要增加和的值,因此左指针右移  
                left++;  
            } else {  
                // 和大于0,说明需要减少和的值,因此右指针左移  
                right--;  
            }  
        }  
    }  
  
    return result;  
};

滑动窗口

无重复字符的最长字串

var lengthOfLongestSubstring = function(s) { 
    let window = {} 
    let left = 0  
    let right = 0
    let res = 0
    while(right<s.length){
        var c = s[right]
        right++
        if(window[c]!=undefined){
            window[c]++
        }else{
            window[c]= 1
        }
        while(window[c]>1){
            let d = s[left]
            left++
            window[d]--
        }
        res = Math.max(res,right-left)
    }
    return res  
};

找到字符串中所有字母异位词

var findAnagrams = function(s, p) {
    // 记录窗口中的字符和需要凑齐的字符:
    let window = new Map()
    let need = new Map()
    // 统计 t 中出现的元素以及它们的个数
    for (var i = 0; i < p.length; i++) {
        var c = p[i];
        if (need.has(c)) {
            need.set(c, need.get(c) + 1);
        } else {
            need.set(c, 1);
        }
    }
    var left = 0, right = 0;
    var valid = 0;
    var res = []; // 记录结果

    while(right<s.length){
        var c = s[right];
        right++;
        // 进行窗口内数据的一系列更新
        if (need.has(c)) {
            if (window.has(c)) {
                window.set(c, window.get(c) + 1);
            } else {
                window.set(c, 1);
            }
            if (need.get(c) === window.get(c)) 
                valid++;
        }
        // 判断左侧窗口是否要收缩
        while (right - left >= p.length) {
            // 当窗口符合条件时,把起始索引加入 res
            if (valid === need.size) {
                res.push(left);
            }
            var d = s[left];
            left++;
            // 进行窗口内数据的一系列更新
            if (need.has(d)) {
                if (window.get(d) === need.get(d))
                    valid--;
                window.set(d, window.get(d) - 1);
            }
        }
    }
    return res;
};

字串

和为K的字数组

var subarraySum = function(nums, k) {  
    let count = 0;  
    let sum = 0;  
    let prefixSum = new Map(); // 用于存储前缀和及其出现的次数  
    prefixSum.set(0, 1); // 初始化前缀和为0的次数为1,用于处理数组开头就存在和为k的情况  
  
    for (let num of nums) {  
        sum += num; // 计算当前前缀和  
  
        // 检查是否存在一个之前的前缀和,使得sum - 该前缀和 = k  
        if (prefixSum.has(sum - k)) {  
            count += prefixSum.get(sum - k); // 累加符合条件的子数组个数  
        }  
  
        // 更新当前前缀和出现的次数  
        if (!prefixSum.has(sum)) {  
            prefixSum.set(sum, 0);  
        }  
        prefixSum.set(sum, prefixSum.get(sum) + 1);  
    }  
  
    return count; 
};

最小覆盖子串

var minWindow = function(s, t) {
    // 哈希表 need 记录需要匹配的字符及对应的出现次数
    // 哈希表 window 记录窗口中满足 need 条件的字符及其出现次数
    let need = new Map();
    let window = new Map();
    for (let i = 0; i < t.length; i++) {
        if (need.has(t[i])) {
            need.set(t[i], need.get(t[i]) + 1);
        } else {
            need.set(t[i], 1);
        }
    }
    let left = 0, right = 0;
    let valid = 0;
    // 记录最小覆盖子串的起始索引及长度
    let start = 0, len = Infinity;
    while (right < s.length) {
        // c 是将移入窗口的字符
        let c = s[right];
        // 扩大窗口
        right++;
        // 进行窗口内数据的一系列更新
        if (need.has(c)) {
            if (window.has(c)) {
                window.set(c, window.get(c) + 1);
            } else {
                window.set(c, 1);
            }
            if (window.get(c) === need.get(c)) {
                valid++;
            }
        }
        // 判断左侧窗口是否要收缩
        while (valid === need.size) {
            // 在这里更新最小覆盖子串
            if (right - left < len) {
                start = left;
                len = right - left;
            }
            // d 是将移出窗口的字符
            let d = s[left];
            // 缩小窗口
            left++;
            // 进行窗口内数据的一系列更新
            if (need.has(d)) {
                if (window.get(d) === need.get(d)) {
                    valid--;
                }
                window.set(d, window.get(d) - 1);
            }
        }
    }
    // 返回最小覆盖子串
    return len === Infinity ? '' : s.substr(start, len);
}

普通数组

最大字数组和

var maxSubArray = function(nums) {  
    let currentSum = nums[0]; // 当前子数组的和,初始化为数组的第一个元素  
    let maxSum = nums[0]; // 迄今为止的最大子数组和,也初始化为数组的第一个元素  
      
    // 从数组的第二个元素开始遍历  
    for (let i = 1; i < nums.length; i++) {  
        // 如果加入当前元素后子数组的和更大,则更新currentSum  
        // 否则,重新开始一个新的子数组(即currentSum只包含当前元素)  
        currentSum = Math.max(nums[i], currentSum + nums[i]);           
        // 更新迄今为止的最大子数组和  
        maxSum = Math.max(maxSum, currentSum);  
    }  
      
    // 返回最大子数组的和  
    return maxSum;  
};

合并区间

  1. 排序:首先根据区间的起始位置对所有区间进行排序,这样可以确保相邻的区间在逻辑上是相邻的,便于后续合并。

  2. 遍历与合并:遍历排序后的区间数组,使用一个 window 变量来维护当前需要合并的区间。对于每个遍历到的区间 item,检查它是否与 window 重叠。如果重叠,则更新 window 的起始和结束位置以包含所有重叠的区间;如果不重叠,则将 window 添加到结果数组 res 中,并将 item 设置为新的 window

  3. 添加最后一个区间:遍历结束后,将最后一个 window 添加到结果数组 res 中,因为在最后一次迭代中,如果最后一个 window 没有与任何后续的区间重叠,它可能不会被添加到 res 中。

  4. 返回结果:返回合并后的区间数组 res

var merge = function(intervals) {
    let res = []
    intervals.sort((a,b)=>{
        return a[0]-b[0]
    })
    let window = intervals[0]
    for(let i=0;i<intervals.length;i++){
        let item = intervals[i]
        // 判断是否与window重叠
        if(item[0]>window[1]){
            res.push(window)
            window = item
        }else{
            window = [Math.min(window[0],item[0]),Math.max(window[1],item[1])]
        }
    }
    res.push(window)
    return res
};

轮转数组

方法一:使用额外数组


var rotate = function(nums, k) {  
    k = k % nums.length; // 确保 k 在数组长度的范围内  
    let temp = nums.slice(nums.length - k)
    let temp2 = nums.slice(0,nums.length - k)

    for(let i=0;i<k;i++){
        nums[i]=temp[i]
    }
    for(let i=k;i<nums.length;i++){
        nums[i]=temp2[i-k]
    }
};

方法二:原地轮转

var rotate = function(nums, k) {  
    const n = nums.length;  
    k = k % n; // 处理k大于数组长度的情况  
    // 定义一个反转函数  
    function reverse(arr, start, end) {  
        while (start < end) {  
            [arr[start], arr[end]] = [arr[end], arr[start]]; // 使用解构赋值来交换元素  
            start++;  
            end--;  
        }  
    }  
    reverse(nums, 0, n - 1);  
    reverse(nums, 0, k - 1);   
    reverse(nums, k, n - 1);
};

除自身以外数组的乘积

var productExceptSelf = function(nums) {
    let left = 1
    let right = 1
    let res = new Array(nums.length).fill(1)
    for(let i=0;i<nums.length;i++){ 
        res[i] = left;   
        left *= nums[i];
    }

    for(let i=nums.length-1;i>=0;i--){
        res[i]*=right
        right *= nums[i]
    }
    return res
};

矩阵

矩阵置零

找到第一个为0的元素的行和列,用那一行和那一列来记录其余行列是否应该清零,最后再处理记录元素的行和列

var setZeroes = function(matrix) {
    let m = matrix.length
    let n = matrix[0].length
    let row = false
    let col = false
    for(let i=0;i<m;i++){
        for(let j=0;j<n;j++){
            if(matrix[i][j]==0){
                if (i == 0) row = true;
                if (j == 0) col = true;
                matrix[0][j] = matrix[i][0] = 0;
            }
        }
    }
    for(let i=1;i<m;i++){
        for(let j=1;j<n;j++){
            if(matrix[0][j]==0 || matrix[i][0]==0 ){
                matrix[i][j]=0
            }
        }
    }

    if (col) {  
            for (let i = 0; i < m; i++) matrix[i][0] = 0;
    } 

    if (row) {  
        for (let j = 0; j < n; j++) matrix[0][j] = 0;
    }  

};

螺旋矩阵

var spiralOrder = function(matrix) {
    if(matrix.length==0){
        return []
    }
    let res = [] //记录结果
    let arrow = [[0,1],[1,0],[0,-1],[-1,0]] //记录方向数组  左到右 上到下 右到左  下到上
    let arrowIndex = 0 //记录当前方向
    let m = matrix[0].length
    let n = matrix.length
    let i=0,j=0

    for(let _=0;_<m*n;_++){
        res.push(matrix[i][j])
        matrix[i][j] = null
        const [di,dj] = arrow[arrowIndex]
        let ni = di+i
        let nj = dj+j
        if(ni<0||ni>=n||nj<0||nj>=m||matrix[ni][nj]==null){
            arrowIndex = (arrowIndex+1)%4
        }
        i+=arrow[arrowIndex][0]
        j+=arrow[arrowIndex][1]
    }
    return res
};

旋转图像

var rotate = function(matrix) {
    const n = matrix.length;  
       
    for (let i = 0; i < n; i++) {  
        for (let j = i; j < n; j++) {   
            [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];  
        }  
    }  
      
    for (let i = 0; i < n; i++) {  
        matrix[i].reverse();  
    }
};

搜索二维矩阵Ⅱ

var searchMatrix = function(matrix, target) {
       if(matrix.lenght== 0) {
            return false;
        }
        let i = 0;
        let j = matrix[0].lenght-1;
        while(i < matrix.length && j >= 0) {
            if(matrix[i][j] == target) {
                return true;
            }
            if(matrix[i][j] < target) {
                i++;
            }
            else {
                j--;
            }
        }
        return false;
};

链表

相交链表

var getIntersectionNode = function (headA, headB) {
    let a = headA
    let b = headB
    while(a!=b){
        if(a){
            a = a.next
        }else{
            a = headB
        }

        if(b){
            b = b.next
        }else{
            b = headA
        }
    }
    return a
};

反转链表

var reverseList = function(head) {
    let prev=null;
    let curr=head;
    let temp=null;
    while(curr!==null){
        temp=curr.next;
        curr.next=prev;
        prev=curr;
        curr=temp;
    }
    return prev;
};

回文链表

var isPalindrome = function(head) {
    let half=halflist(head);  //找到找到前半部分的尾结点
    let halfhead=reverlist(half.next); //反转后半部分链表 应该是half.next 不能是half
    //判断是否回文
    //定义双指针 一个从头开始 一个从中间开始
    let p1=head;
    let p2=halfhead;
    let flag=true;
    while(flag && p2!=null){
        if(p1.val!=p2.val){
            flag=false;
        }
        p1=p1.next;
        p2=p2.next;
    }
    return flag;
};
//查找中间结点 定义快慢指针 查找中间结点  
//如果是奇数结点 则为最中间 如果是偶数结点 则为前半部分最后一个结点
const halflist=(head)=>{
    let fast=head;
    let slow=head;
    while(fast.next!=null&&fast.next.next!=null){
        slow=slow.next;
        fast=fast.next.next;
    }
    return slow;
}
//反转链表
const reverlist = (head)=>{
    let prev=null;
    let curr=head;
    let temp=null;
    while(curr!==null){
        temp=curr.next;
        curr.next=prev;
        prev=curr;
        curr=temp;
    }
    return prev;
}

环形链表

var hasCycle = function(head) {
    let set = new Set()
    let a = head
    while(a!=null){
        if(set.has(a)){
            return true
        }
        set.add(a)
        a = a.next
    }
    return false
};

双指针

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

环形链表Ⅱ

var detectCycle = function(head) {
    let set = new Set()
    let a = head
    while(a!=null){
        if(set.has(a)){
            return a
        }
        set.add(a)
        a = a.next
    }
    return a
};

双指针

var detectCycle = function(head) {
    if (!head || !head.next) {  
        return null; // 空链表或只有一个节点的链表没有环  
    }  
      
    let slow = head;  
    let fast = head;  
      
    // 先判断是否存在环  
    while (fast && fast.next) {  
        slow = slow.next;  
        fast = fast.next.next;  
        if (slow === fast) {  
            break; // 快慢指针相遇,说明有环  
        }  
    }  
      
    // 如果没有环,fast会先到达链表末尾  
    if (!fast || !fast.next) {  
        return null;  
    }  
      
    // 将慢指针移回链表头部,快慢指针每次各移动一步  
    slow = head;  
    while (slow !== fast) {  
        slow = slow.next;  
        fast = fast.next;  
    }  
      
    // 当快慢指针再次相遇时,它们相遇的点就是环的起始点  
    return slow;
};

合并两个升序链表

var mergeTwoLists = function (l1, l2) {
    const prehead = new ListNode();
    let res=prehead
    while (l1 != null && l2 != null) {
        if (l1.val < l2.val) {
            res.next = l1
            l1 = l1.next
        } else {
            res.next = l2
            l2 = l2.next
        }
        res = res.next
    }
    res.next = l1 == null ? l2 : l1
    return prehead.next
};

两数相加

var addTwoNumbers = function(l1, l2) {   
    let ret = 0; // 保存进位  
    let header = new ListNode(0); // 创建一个哑节点作为结果链表的头部  
    let res = header; // 指针,用于构建结果链表  

    while (l1 != null || l2 != null || ret > 0) {  
        let num1 = l1 ? l1.val : 0;  
        let num2 = l2 ? l2.val : 0;  
        let sum = num1 + num2 + ret;  
        // 创建新节点并添加到结果链表  
        let temp = new ListNode(sum % 10);  
        res.next = temp;  
        res = res.next;  
        // 更新进位  
        ret = Math.floor(sum / 10);  
        // 移动链表指针  
        if (l1 !== null) l1 = l1.next;  
        if (l2 !== null) l2 = l2.next;  
        
        if(ret==0&&(l1 == null || l2 == null)){
            res.next = l1==null?l2:l1
            return header.next
        }
    }  
  
    return header.next; // 返回哑节点的下一个节点  
};

删除链表的倒数第N个结点

var removeNthFromEnd = function(head, n) {  

    //p q均指向head
    var p = head,q = head;
    //如果链表为空或者只有一个结点,删完之后只能为空
    if(head == null || head.next == null) return null;
    //让p先右移n次
    for(let i = n;i > 0;i--){
        p = p.next;
    }
    //p q同时右移,直到p只想最后一个结点
    while(p != null && p.next != null){
        q = q.next;
        p = p.next;
    }
    //如果P为空,即要删除的是链表的第一个结点。 
    if(p == null){
        head = q.next;
    }else{    //不是删除第一个结点就直接用q删除next。。
        q.next = q.next.next;
    }   
    
    return head;
};  

两两交换链表中的结点

var swapPairs = function(head) {
    let dummy = new ListNode(0);  
    dummy.next = head; 
    if(dummy.next==null||dummy.next.next==null){
        return head
    }
    let slow = dummy
    let curl = dummy.next
    let fast = dummy.next.next
    while(curl!=null&&fast!=null){
        slow.next = fast
        curl.next = fast.next
        fast.next = curl
        slow = curl
        if(!slow.next||!slow.next.next){
            break
        }
        curl = slow.next
        fast = slow.next.next
    }
    return dummy.next
};

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值