仅仅是做个打卡,保持每天刷题。
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 开头。
思路:
- 两个链表长度不一致,需要想办法对齐 可以通过null就不往后走,val直接给0的方式
- 每次生成一个临时的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 ,请你找出其中不含有重复字符的 最长子串 的长度。
思路:
- 一次遍历
- 每次都计算当前和左边界的最大值
- 当新来的在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)) 。
思路:
- 每次淘汰最小的k/2个数,递归查询下一个数
- n1长度小,n2长度大
- 判断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 中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
思路:
- 中心扩散法
- 每次用i,i和i,i+1去扩散
- 如果这次扩散后的最大,那么可以更新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);
思路:
- 模拟法
- 新建一个数组,共numRows行
- 然后做个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 位整数(有符号或无符号)。
思路:
- 模拟法
- js处理负数不方便,用flag控制
- 对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 。 返回整数作为最终结果。 注意:
本题中的空白字符只包括空格字符 ' ' 。 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
思路:
- 偷懒了,当练练正则就是了
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不是。
思路:
- 引入一个revertedNumber
- 反转一般x 〈= revertedNumber就可以停止判断了
- 然后考虑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的,而不是部分字符串。
思路:
- 引入一个revertedNumber
- 反转一般x 〈= revertedNumber就可以停止判断了
- 然后考虑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))
}
};