Leetcode 1-10

72 阅读5分钟

仅仅是做个打卡,保持每天刷题。

1. 两数之和

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

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

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

思路:

哈希表存储是否有target - nums[i]

function twoSum(nums: number[], target: number): number[] { 
    const map=new Map()
    for(let i=0;i<nums.length;i++) {
        // 每次访问都存下来,target - nums[i]存在就说明找到目标值了
        if(map.get(nums[i]) !== undefined){
            return [map.get(nums[i]), i]
        } else {
            map.set(target-nums[i], i)
        }
    }
    return []
};

2. 两数相加

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

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

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

思路:

  1. 两个链表长度不一致,需要想办法对齐 可以通过null就不往后走,val直接给0的方式
  2. 每次生成一个临时的ListNode,不断往下推进
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function addTwoNumbers(l1: ListNode | null, l2: ListNode | null): ListNode | null {
    const dummy = new ListNode(-1, null)
    let cur = dummy
    let carry = 0

    while(l1 !== null || l2 !== null) {
        const n1 = l1?.val ?? 0, n2 = l2?.val ?? 0
        const sum = n1 + n2 + carry
        carry = sum > 9 ? 1 : 0

        cur.next = new ListNode(sum % 10)
        cur = cur.next

        if(l1 !== null) l1=l1.next
        if(l2 !== null) l2=l2.next
    }
    if(carry === 1) cur.next = new ListNode(1,null)
    return dummy.next
};

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

思路:

  1. 一次遍历
  2. 每次都计算当前和左边界的最大值
  3. 当新来的在map中存在且这个值大于等于左边界,则更新左边界
 function lengthOfLongestSubstring(s: string): number {
    let ans = 0
    const map = {}
    let left = 0
    for(let i=0;i<s.length;i++) {
        const c = s[i]
        if(map[c] !== undefined && map[c] >= left) {
            left = map[c] + 1
        }
        map[c] = i
        ans = Math.max(ans, i-left+1)
    }
    return ans
};

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

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

思路:

  1. 每次淘汰最小的k/2个数,递归查询下一个数
  2. n1长度小,n2长度大
  3. 判断n1等于0
function findMedianSortedArrays(nums1: number[], nums2: number[]): number {
    const n = nums1.length
    const m = nums2.length
    let left = Math.floor((n+m+1)/2)
    let right = Math.floor((n+m+2)/2)

    function getKth(nums1: number[], start1: number, end1: number, nums2: number[], start2: number, end2: number, k: number): number{
        const len1 = end1-start1+1
        const len2 = end2-start2+1

        if(len1>len2) return getKth(nums2,start2,end2,nums1,start1,end1,k)
        if(len1===0) return nums2[start2+k-1]

        if(k === 1) return Math.min(nums1[start1], nums2[start2])

        const i = start1 + Math.min(len1, Math.floor(k/2)) - 1
        const j = start2 + Math.min(len2, Math.floor(k/2)) - 1

        if (nums1[i] > nums2[j]) {
            return getKth(nums1,start1,end1,nums2,j+1,end2,k-(j-start2+1))
        } else {
            return getKth(nums1,i+1,end1,nums2,start2,end2,k-(i-start1+1))
        }

    }
    const m1 = getKth(nums1, 0, n-1, nums2, 0, m-1, left)
    const m2 = getKth(nums1, 0, n-1, nums2, 0, m-1, right)
    return (m1 + m2) / 2
    
};

5. 最长回文子串

难度中等6243收藏分享切换为英文接收动态反馈

给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

思路:

  1. 中心扩散法
  2. 每次用i,i和i,i+1去扩散
  3. 如果这次扩散后的最大,那么可以更新answer
function longestPalindrome(s: string): string {
    let res = ''
    let max = 0
    function help(left: number,right: number){
        while(left>=0 && right<s.length && s[left] === s[right]) {
            left--
            right++
        }
        if(right-left-1 > max){
            res = s.substring(left+1, right)
            max = right-left-1
        }
    }
    for(let i=0;i<s.length;i++) {
        help(i, i)
        if(i+1<s.length){
            help(i, i+1)
        }
    }

    return res
};

6. N 字形变换

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

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

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

思路:

  1. 模拟法
  2. 新建一个数组,共numRows行
  3. 然后做个flag判断是往下还是往上,0或numRows-1的时候换
function convert(s: string, numRows: number): string {
    if(numRows<2) return s
    const res: string[] = new Array(numRows).fill('')
    let i=0;
    let flag = -1;
    for(let c of s) {
        res[i] += c
        if(i === 0 || i === numRows -1) flag = - flag;
        i+=flag
    }
    return res.join('')
};

7. 整数反转

难度中等3810收藏分享切换为英文接收动态反馈

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231,  231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

思路:

  1. 模拟法
  2. js处理负数不方便,用flag控制
  3. 对x不断取最小位数,赋给res,然后右移
function reverse(x: number): number {
    const flag = x >=0 ? 1 : -1
    let num = Math.abs(x)
    let res = 0
    while(num !== 0) {
        const d = num % 10
        num = Math.floor(num/10)
        res = res * 10 + d
        if (res < Math.pow(-2, 31) || res > Math.pow(2, 31) - 1) {
            return 0;
        }
    }

    return flag * res
};

8. 字符串转换整数 (atoi)

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

读入字符串并丢弃无用的前导空格 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。 如果整数数超过 32 位有符号整数范围 [−231,  231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。 返回整数作为最终结果。 注意:

本题中的空白字符只包括空格字符 ' ' 。 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

思路:

  1. 偷懒了,当练练正则就是了
function myAtoi(x: number): number {
    const flag = x >=0 ? 1 : -1
    let num = Math.abs(x)
    let res = 0
    while(num !== 0) {
        const d = num % 10
        num = Math.floor(num/10)
        res = res * 10 + d
        if (res < Math.pow(-2, 31) || res > Math.pow(2, 31) - 1) {
            return 0;
        }
    }

    return flag * res
};

9. 回文数

难度简单2494收藏分享切换为英文接收动态反馈

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

  • 例如,121 是回文,而 123 不是。

思路:

  1. 引入一个revertedNumber
  2. 反转一般x 〈= revertedNumber就可以停止判断了
  3. 然后考虑x不可能会回文的区间+对比x和revertedNumber就出来了
function isPalindrome(x: number): boolean {
    if(x < 0 || (x !== 0 && x % 10 === 0)) return false
    let revertedNumber = 0
    while(x > revertedNumber){
        revertedNumber = revertedNumber * 10 + x % 10
        x = Math.floor(x / 10)
    }
    return revertedNumber === x || Math.floor(revertedNumber / 10) === x
};

10. 正则表达式匹配

难度困难

3594

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

  • '.' 匹配任意单个字符
  • '*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

思路:

  1. 引入一个revertedNumber
  2. 反转一般x 〈= revertedNumber就可以停止判断了
  3. 然后考虑x不可能会回文的区间+对比x和revertedNumber就出来了
function isMatch(s: string, p: string): boolean { 
    if(p.length === 0){ return !s.length } 
    let match = false 
    if(s.length > 0 && (s[0] === p[0] || p[0] === '.')){
        match = true 
    } 
    if(p.length > 1 && p[1] === '*') { 
        return isMatch(s, p.slice(2)) || (match && isMatch(s.slice(1), p)) 
    } else { 
        return match && isMatch(s.slice(1), p.slice(1)) 
    }
};