二十天刷leetcode【hot100】算法- day2[前端Typescript]

指针

6.三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。
在这里插入图片描述
leetcode 15. 三数之和

题解

该题需要先从小到大排序,后需要建立三个指针,从头到尾完成遍历,第一层遍历决定第一个元素(first),第二层遍历决定第二个元素(second = first + 1)跟第三个元素(third,初始化为最大的元素)。其中对于确认了first后,就要对secondthird进行首尾遍历。原则为:三者相加,大于target则左移third,小于则右移second。另外需要注意,不能输出相同的元素,又因为该数组一开始就经过排序,则可以跳过相等的两个元素。

function threeSum(nums: Array<number>): Array<Array<number>> {
    const res: Array<Array<number>> = [];
    // 先进行排列
    nums.sort((a, b) => a - b);
    for(let first = 0; first < nums.length; first ++) {
        // 第一层遍历
        if (first > 0 && nums[first] === nums[first - 1]) {
            // 相邻的两个元素相等,则跳过
            continue;
        }
    
        // 第三个元素初始化为排列后的最后一个,即数组最大值
        let third = nums.length - 1;
        // tagget + nums[first] = 0
        const target = -1 * nums[first];
        
        // 第二个元素初始化为第一个元素的后一个元素
        for(let second = first + 1; second < nums.length - 1; second ++) {
            // 对数组进行《首尾收缩遍历》
            if (second > first + 1 && nums[second] === nums[second - 1]) {
                // 相邻的两个元素相等,则跳过
                continue;
            }
            
            while(second < third && nums[second] + nums[third] > target) {
                // 第二第三元素相加大于target,则向头部收缩
                third--;
            }
            
            if (second === third) {
                // 相等了,则直接跳去循环
                // 因为元素不能相等,则该数组经过排序,若相等,后面的《首尾收缩遍历》都会相等
                break
            } else if (nums[second] + nums[third] === target) {
                res.push([nums[first], nums[second], nums[third]]);
            }
        }
    }
    return res;
};
    
// const arr = threeSum([-1, 0, 1, 2, -1, -4]);
// // { arr: [ [ -1, -1, 2 ], [ -1, 0, 1 ] ] }
// console.log({ arr });

7.最接近的两数和 - 字节-上海

给出一个数组和一个目标值,找出两个和最接近目标值的子项

题解

数组从大到小排序,双指针首尾收缩遍历,当前两者相加大于目标则收缩尾部,小于则收缩头部,用res记录最接近目标值的值

function testNear (arrNear: Array<number>, targetNear: number): number {
    // 排序
    arrNear.sort((a, b) => a - b);
    let left = 0, right = arrNear.length - 1;
    let res: number = 0;
    while(left < right) {
        // 这里的结束边界不能是left <= right
        // 因为arrNear[left]和arrNear[right]需要是不同的元素
        // 《首尾收缩遍历》
        const temp = arrNear[left] + arrNear[right];
        if(temp === targetNear) {
            return res;
        } else if (temp > targetNear) {
            // 尾部收缩
            right--;
        } else {
            // 头部收缩
            left++;
        }
        // 更新最接近的值
        res = Math.abs(targetNear - res) > Math.abs(targetNear - temp) ? temp : res;
    }

    return res;
}

// const arrNear = [24,69,14,37];
// const targetNear = 60;
// const nearP = testNear(arrNear, targetNear);
// console.log({ nearP }); // { nearP: 61 }

8 接雨水 - 腾讯cdg

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
在这里插入图片描述
leetcode 42. 接雨水

题解
一:动态规划

dp数组记录下标i及其左(leftMax )右(rightMax)边的所有柱子的最大高度。dp数组初始化,leftMax[0] = height[0];rightMax[len - 1] = height[len - 1];。dp数组遍历过程中,左右侧的值与当值的高度进行对比,更新dp数组

leftMax[i] = Math.max(leftMax[i - 1], height[i]);
rightMax[j] = Math.max(rightMax[j + 1], height[j]);

左右侧最高的两条柱子中,矮的那条减自身高度,即为当前柱子能接的水

function trapDp(height: Array<number>): number {
    // 动态规划
    const len = height.length;
    if (len === 0) {
        return 0;
    }
    // 下标i及其左边的所有柱子的最大高度
    const leftMax = Array(len).fill(0);
    // 下标i及其右边的所有柱子的最大高度
    const rightMax = Array(len).fill(0);

    // 初始化
    leftMax[0] = height[0];
    rightMax[len - 1] = height[len - 1];

    for(let i = 1; i < len; i++) {
        // 更新柱子i 左侧(包括自身)的最高高度
        leftMax[i] = Math.max(leftMax[i - 1], height[i]);
    }

    for(let j = len - 2; j >= 0; j--) {
        // 更新柱子i 右侧(包括自身)的最高高度
        rightMax[j] = Math.max(rightMax[j + 1], height[j]);
    }

    let ans = 0;

    for (let k = 0; k < len; k++) {
        // 左右侧最高的两条柱子中,矮的那条减自身高度,即为当前柱子能接的水
        ans += Math.min(leftMax[k], rightMax[k]) - height[k];
    }

    return ans;
};

// const e = trapDp([0,1,0,2,1,0,1,3,2,1,2,1]);
// console.log({ e }); // 6
二、双指针

头尾双指针收缩遍历,详细见代码解

function trap(height: Array<number>): number {
    // 双指针
    const len = height.length;

    let left = 0, right = height.length - 1;
    // 初始化左右最大值
    let leftMax = height[0], rightMax = height[len - 1];

    let ans = 0;
    while(left < right) {
        // 这里循环不是为了找到某条柱子,而是要经过所有的柱子即可
        // 所有不用left <= right
        // 左侧最大值与左指针相比,更新最大值
        leftMax = Math.max(leftMax, height[left]);
        // 右侧最大值与左指针相比,更新最大值
        rightMax = Math.max(rightMax, height[right]);

        // 移动较矮的指针(因为矮的指针决定能存多少水--木桶原理),累加可以积累的雨水
        if (height[left] < height[right]) {
            ans += leftMax - height[left];
            // 左指针比较小,移动左指针
            left++;
        } else {
            ans += rightMax - height[right];
            right--;
        }
    }
    return ans;
};

// const f = trap([0,1,0,2,1,0,1,3,2,1,2,1]);
// console.log({ f }); // { f: 6 }

9 无重复字符串的最长子串

给定一个字符串s ,请你找出其中不含有重复字符的 最长
子串的长度。
在这里插入图片描述
leetcode 3.无重复字符串的最长子串

题解

双指针搭配set, 用set去重,左指针移动, set移除元素,右指针移动,且右指针元素不存在于set中,则加入set,最后根据左右指针位置更新最大长度

function lengthOfLongestSubstring(s: string): number {
    // 左右指针
    // 用set去重
    const setNew = new Set();
    let right = -1;

    let ans = 0;

    for(let left = 0; left < s.length; left++) {
    if (left > 0) {
        // 左指针移动, set移除左指针
        setNew.delete(s[left - 1]);
    }

    // 右指针移动
    while(right + 1 < s.length && !setNew.has(s[right + 1])) {
    // 右指针不超过边界,且右指针元素不存在于set中,则加入set
    setNew.add(s[right + 1]);
    right++;
    }

    // 根据左右指针位置更新最大长度
    ans = Math.max(ans, right - left + 1);
    }

    return ans;
};

// const g = lengthOfLongestSubstring('abcabcbb');
// // { g: 3 }
// console.log({ g });

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

给定两个字符串 sp,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
在这里插入图片描述
leetcode 438.找到字符串中所有字母异位词

题解

使用《位置数组》记录当前值在英文字母中的顺序。初始化遍历先把p字符串和s下标0pLen - 1的子串遍历完。如这个时候如果位置数组相等,则把下标0推进数组。后将s剩下的数据遍历完成。

function findAnagrams(s: string, p: string): Array<number> {
    const sLen = s.length, pLen = p.length;

    if (sLen < pLen) {
        // s长度小于p长度,s中不存在p的异位词子串
        return [];
    }

    const ans: Array<number> = [];
    // 初始化位置数组
    const sCount = new Array(26).fill(0);
    const pCount = new Array(26).fill(0);
    for (let i = 0; i < pLen; ++i) {
        // 初始化遍历
        // 用《位置数组》记录当前值在英文字母中的顺序
        // 先把p字符串和s下标0到pLen - 1的子串遍历完
        ++sCount[s[i].charCodeAt(0) - 'a'.charCodeAt(0)];
        ++pCount[p[i].charCodeAt(0) - 'a'.charCodeAt(0)];
    }

    if (sCount.toString() === pCount.toString()) {
        // 把p字符串和s下标0到pLen - 1的子串遍历完了,如这个时候如果位置数组相等,则把下标0推进数组
        ans.push(0);
    }

    for (let i = 0; i < sLen - pLen; ++i) {
        // 将s剩下的数据遍历完成
        // 将左侧的移除
        --sCount[s[i].charCodeAt(0) - 'a'.charCodeAt(0)];
        // 加入右侧新遍历的(中间差了初始化遍历的pLen个值)
        ++sCount[s[i + pLen].charCodeAt(0) - 'a'.charCodeAt(0)];

        if (sCount.toString() === pCount.toString()) {
            // 位置数组相等,则把下标推进数组
            ans.push(i + 1);
        }
    }

    return ans;
};

// const arr = findAnagrams('cbaebabacd', 'abc');
// // { arr: [ 0, 6 ] }
// console.log({ arr });

关注我的公众号,回复 100905A1 获取hot100算法在线链接
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值