functiontwoSum(numbers: number[], target: number): number[] { let l = 0, r = numbers.length - 1 while (l < r) { const sum = numbers[l] + numbers[r] if (sum === target) return [l + 1, r + 1] elseif (sum < target) l++ else r-- } return [] }
和为 k 的子数组
利用前缀和 + 哈希表来完成这个工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
functionsubarraySum(nums: number[], k: number): number { // <prefixSum, count> const hashMap = newMap<number, number>() hashMap.set(0, 1) let sum = 0 let ans = 0 for (const num of nums) { sum += num // 找到满足k的部分子数组 if (hashMap.has(sum - k)) { ans += hashMap.get(sum - k) asnumber } // update hashMap.set(sum, hashMap.has(sum) ? (hashMap.get(sum) asnumber) + 1 : 1) } return ans }
K 和数对的最大数目
1 2 3 4 5 6 7 8 9 10 11 12 13 14
functionmaxOperations(nums: number[], k: number): number { const hashMap = newMap<number, number>() let ans = 0 for (const num of nums) { const cnt = hashMap.get(k - num) if (cnt) { hashMap.set(k - num, cnt - 1) ans++ } else { hashMap.set(num, (hashMap.get(num) ?? 0) + 1) } } return ans }
functionthreeSum(nums: number[]): number[][] { constans: number[][] = [] nums = nums.sort((a, b) => a - b) for (let i = 0; i < nums.length; i++) { if (i && nums[i] === nums[i - 1]) continue if (nums[i] > 0) continue let l = i + 1, r = nums.length - 1 while (l < r) { const sum = nums[i] + nums[l] + nums[r] if (sum === 0) { ans.push([nums[i], nums[l], nums[r]]) while (l < r && nums[l] === nums[++l]) {} while (l < r && nums[r] === nums[--r]) {} } elseif (sum < 0) l++ else r-- } } return ans }
functionlongestConsecutive(nums: number[]): number { nums = nums.sort((a, b) => a - b) const hashMap = newMap<number, number>() for (const num of nums) { if (hashMap.has(num - 1)) { hashMap.set(num, (hashMap.get(num - 1) asnumber) + 1) } else { hashMap.set(num, 1) } } returnMath.max(...hashMap.values(), 0) }
移动零
双指针 移动 0 到尾
1 2 3 4 5 6 7 8 9 10 11 12 13
functionmoveZeroes(nums: number[]): void { let i = 0, j = 0 while (j < nums.length) { if (nums[j] !== 0) { if (i !== j) { ;[nums[i], nums[j]] = [nums[j], nums[i]] } i++ } j++ } }
盛水最多的容器
双指针判断比较
1 2 3 4 5 6 7 8 9 10 11 12
functionmaxArea(height: number[]): number { let max = 0 let l = 0, r = height.length - 1 while (l < r) { max = height[l] > height[r] ? Math.max(max, (r - l) * height[r--]) : Math.max(max, (r - l) * height[l++]) } return max }
functiontrap(height: number[]): number { let ans = 0 if (height.length < 3) return ans const maxHeight = Math.max(...height) let leftMaxIndex = 0 let rightMaxIndex = height.length - 1 for (let i = 0; i < height.length; i++) { if (height[i] === maxHeight) { leftMaxIndex = i break } } for (let i = height.length - 1; i >= 0; i--) { if (height[i] === maxHeight) { rightMaxIndex = i break } } // 计算left - right 之间的 for (let i = leftMaxIndex + 1; i < rightMaxIndex; i++) { ans += maxHeight - height[i] } // 计算0 - left 之间的 let limit = height[0] for (let i = 1; i <= leftMaxIndex; i++) { if (height[i] < limit) { ans += limit - height[i] } else { limit = height[i] } } // 计算right - height.length - 1 limit = height[height.length - 1] for (let i = height.length - 2; i >= rightMaxIndex; i--) { if (height[i] < limit) { ans += limit - height[i] } else { limit = height[i] } } return ans }
无重复最长子串
1 2 3 4 5 6 7 8 9 10 11 12 13 14
functionlengthOfLongestSubstring(s: string): number { let max = 0 let temp = '' for (const ch of s) { if (!temp.includes(ch)) { temp += ch } else { temp = temp.slice(temp.indexOf(ch) + 1) temp += ch } max = Math.max(max, temp.length) } return max }
const getStringId = (s: string): number => { return s >= 'A' && s <= 'Z' ? s.charCodeAt(0) - 'A'.charCodeAt(0) + 26 : s.charCodeAt(0) - 'a'.charCodeAt(0) }
functionminWindow(s: string, t: string): string { let ans = '' // 目标数组的频率 const targetFrequentArr = Array(60).fill(0) // 滑动窗口的频率 const windowFrequentArr = Array(60).fill(0) // 字母数 let count = 0 for (const ch of t) { if (++targetFrequentArr[getStringId(ch)] === 1) { count++ } } for (let l = 0, r = 0; r < s.length; r++) { const indexR = getStringId(s[r]) if (++windowFrequentArr[indexR] === targetFrequentArr[indexR]) { count-- } while (l < r) { const indexL = getStringId(s[l]) if (windowFrequentArr[indexL] > targetFrequentArr[indexL]) { windowFrequentArr[indexL]-- if (windowFrequentArr[indexL] >= 0) l++ } else { break } } if (count === 0 && (!ans || ans.length > r - l + 1)) { ans = s.slice(l, r + 1) } } return ans }
最大子数组和
mybe 类似于 dp ?
1 2 3 4 5 6 7
functionmaxSubArray(nums: number[]): number { const dp = [...nums] for (let i = 1; i < dp.length; i++) { dp[i] = Math.max(dp[i], dp[i] + dp[i - 1]) } returnMath.max(...dp) }
合并区间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
functionmerge(intervals: number[][]): number[][] { constans: number[][] = [] intervals = intervals.sort((a, b) => a[0] - b[0]) let temp = intervals[0] for (let i = 1; i < intervals.length; i++) { if (temp[1] < intervals[i][0]) { ans.push(temp) temp = intervals[i] } else { if (temp[1] < intervals[i][1]) { temp = [temp[0], intervals[i][1]] } } } ans.push(temp) return ans }
除自身以外数组的乘积
1 2 3 4 5 6 7 8 9 10 11 12 13 14
functionproductExceptSelf(nums: number[]): number[] { constans: number[] = Array(nums.length).fill(1) const help = [...nums] for (let i = 1; i < help.length; i++) { help[i] = help[i - 1] * help[i] } let right = 1 for (let i = nums.length - 1; i >= 1; i--) { ans[i] = help[i - 1] * right right = right * nums[i] } ans[0] = right return ans }
缺失的第一个正数
数组技巧
1 2 3 4 5 6 7 8 9 10 11
functionfirstMissingPositive(nums: number[]): number { constrecord: number[] = [1] for (const num of nums) { if (num > 0) { record[num] = 1 } } return record.findIndex((item) => !item) === -1 ? record.length : record.findIndex((item) => !item) }
functionspiralOrder(matrix: number[][]): number[] { constans: number[] = [] const m = matrix.length, n = matrix[0].length let l = 0, r = n - 1, top = 0, bottom = m - 1 while (l <= r && top <= bottom) { // forEach top for (let i = l; i <= r && top <= bottom; i++) { ans.push(matrix[top][i]) } top++ // Right for (let i = top; i <= bottom && l <= r; i++) { ans.push(matrix[i][r]) } r-- // Bottom for (let i = r; i >= l && top <= bottom; i--) { ans.push(matrix[bottom][i]) } bottom-- // Left for (let i = bottom; i >= top && l <= r; i--) { ans.push(matrix[i][l]) } l++ } return ans }
functionreverseList(head: ListNode | null): ListNode | null { letans: ListNode | null = null let cur = head while (cur) { const next = cur.next cur.next = ans ans = cur cur = next } return ans }
回文链表
可以转化成数组判断 || 使用 stack 栈来判断
1 2 3 4 5 6 7 8 9 10 11 12
functionisPalindrome(head: ListNode | null): boolean { constarr: number[] = [] let cur = head while (cur) { arr.push(cur.val) cur = cur.next } for (let i = 0; i < Math.floor(arr.length / 2); i++) { if (arr[i] !== arr[arr.length - 1 - i]) returnfalse } returntrue }
classListNode { val: number next: ListNode | null constructor(val?: number, next?: ListNode | null) { this.val = val || 0 this.next = next || null } }
constreveseListNode = (head: ListNode | null, tail: ListNode | null) => { letans: ListNode | null = null let cur = head while (cur && cur !== tail) { const next = cur.next cur.next = ans ans = cur cur = next } return ans } functionreverseKGroup(head: ListNode | null, k: number): ListNode | null { let start = head let end = head for (let i = 0; i < k; i++) { if (!end) return head end = end.next } const reverserList = reveseListNode(start, end) start!.next = reverseKGroup(end, k) return reverserList }
functionsortList(head: ListNode | null): ListNode | null { letarr: number[] = [] while (head) { arr.push(head.val) head = head.next } arr = arr.sort((a, b) => a - b) let ans = newListNode(0) let cur = ans for (const num of arr) { let newNode = newListNode(num) cur.next = newNode cur = cur.next } return ans.next }
合并 K 个升序链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
functionmergeKLists(lists: Array<ListNode | null>): ListNode | null { letlistsArr: number[] = [] for (let list of lists) { while (list) { listsArr.push(list.val) list = list.next } } listsArr = listsArr.sort((a, b) => a - b) const ans = newListNode(0) let cur = ans for (const num of listsArr) { const newNode = newListNode(num) cur.next = newNode cur = cur.next } return ans.next }
functiondecodeString(s: string): string { conststack: string[] = [] for (const ch of s) { if (ch !== ']') { stack.push(ch) } else { let preChar = '' while (stack.length && stack[stack.length - 1] !== '[') { preChar = stack.pop() + preChar } stack.pop() let num = '' while (!isNaN(Number(stack[stack.length - 1]))) { num = stack.pop() + num } stack.push(preChar.repeat(Math.max(1, Number(num)))) } } return stack.join('') }
每日温度
在计算的过程中 维护一个单调栈既可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
functiondailyTemperatures(temperatures: number[]): number[] { const n = temperatures.length constans: number[] = Array(n).fill(0) conststack: number[] = [] for (let i = n - 1; i >= 0; i--) { const t = temperatures[i] while (stack.length && temperatures[stack[stack.length - 1]] <= t) { stack.pop() } if (stack.length) { ans[i] = stack[stack.length - 1] - i } stack.push(i) } return ans }
买股票最佳时间
不断的更新最大值和最小值
1 2 3 4 5 6 7 8 9
functionmaxProfit(prices: number[]): number { let maxPrice = 0 let minPrice = prices[0] for (const price of prices) { minPrice = Math.min(price, minPrice) maxPrice = Math.max(price - minPrice, maxPrice) } return maxPrice }
跳跃游戏
维护一个最大的跳远变量去循环数组一一比较, 更新
1 2 3 4 5 6 7 8
functioncanJump(nums: number[]): boolean { let maxJumpStep = 0 for (let i = 0; i < nums.length; i++) { if (maxJumpStep < i) returnfalse maxJumpStep = Math.max(maxJumpStep, nums[i] + i) } returntrue }
1 2 3 4 5 6 7 8 9 10
// 维护一个最大的[dp] 找出满足的j去构建i functionjump(nums: number[]): number { const n = nums.length constdp: number[] = Array(n).fill(0) for (let i = 1, j = 0; i < n; i++) { while (nums[j] + j < i) j++ dp[i] = dp[j] + 1 } return dp[n - 1] }
划分字母区间
找出最大的 index 去遍历逐一比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
functionpartitionLabels(s: string): number[] { constans: number[] = [] constrecordLastIndex: Record<string, number> = {} for (let i = 0; i < s.length; i++) { recordLastIndex[s[i]] = i } let l = 0, r = 0 for (let i = 0; i < s.length; i++) { r = Math.max(r, recordLastIndex[s[i]]) if (r === i) { ans.push(r - l + 1) l = r + 1 } } return ans }
functionlengthOfLIS(nums: number[]): number { const n = nums.length constdp: number[] = Array(n).fill(1) for (let i = 1; i < nums.length; i++) { for (let j = 0; j < i; j++) { if (nums[j] < nums[i]) { dp[i] = Math.max(dp[i], dp[j] + 1) } } } returnMath.max(...dp) }
乘积最大子数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14
functionmaxProduct(nums: number[]): number { let max = nums[0] let min = nums[0] let maxmax = nums[0] for (let i = 1; i < nums.length; i++) { if (nums[i] < 0) { ;[max, min] = [min, max] } max = Math.max(nums[i], max * nums[i]) min = Math.min(nums[i], min * nums[i]) maxmax = Math.max(max, maxmax) } return maxmax }
不同路径
1 2 3 4 5 6 7 8 9
functionuniquePaths(m: number, n: number): number { constdp: number[][] = Array.from({ length: m }, () =>Array(n).fill(1)) for (let i = 1; i < m; i++) { for (let j = 1; j < n; j++) { dp[i][j] = dp[i - 1][j] + dp[i][j - 1] } } return dp[m - 1][n - 1] }
最小路径和
1 2 3 4 5 6 7 8 9 10 11 12 13
functionminPathSum(grid: number[][]): number { const m = grid.length, n = grid[0].length // init for (let i = 1; i < m; i++) grid[i][0] = grid[i][0] + grid[i - 1][0] for (let j = 1; j < n; j++) grid[0][j] = grid[0][j] + grid[0][j - 1] for (let i = 1; i < m; i++) { for (let j = 1; j < n; j++) { grid[i][j] = Math.min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j] } } return grid[m - 1][n - 1] }
functionlongestPalindrome(s: string): string { let max = 0 let start = -1 for (let i = 0; i < s.length; i++) { let now = 1 let l = i - 1 while (s[i + 1] === s[i]) { now++ i++ } let r = i + 1 while (s[l] && s[r] && s[l] === s[r]) { l-- r++ now += 2 } if (now > max) { max = now start = l + 1 } } return s.slice(start, start + max) }
只出现一次的数字
利用技巧 ^ 相同的数字为 0 0 与任何数相^ 等于任何数
1 2 3 4 5 6 7
functionsingleNumber(nums: number[]): number { let ans = 0 for (const num of nums) { ans ^= num } return ans }
多数元素
1 2 3 4 5 6 7 8 9 10 11
functionmajorityElement(nums: number[]): number { let count = 0 let candiate = nums[0] for (const num of nums) { if (count === 0) { candiate = num } count += num === candiate ? 1 : -1 } return candiate }
颜色分类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
functionsortColors(nums: number[]): void { let l = 0, r = nums.length - 1, index = 0 while (index <= r) { if (nums[index] === 0) { ;[nums[l], nums[index]] = [nums[index], nums[l]] l++ index++ } elseif (nums[index] === 1) { index++ } else { ;[nums[index], nums[r]] = [nums[r], nums[index]] r-- } } }
下一个排列
找出下降点 || 交换下降点和第一个大于他的数字 || 升序下降点之后的序列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
functionnextPermutation(nums: number[]): void { let descrNum = nums.length - 1 // 找到下降点 while (descrNum > 0 && nums[descrNum - 1] >= nums[descrNum]) descrNum-- // 找到需要交换的元素 descrNum-- if (descrNum >= 0) { let exChangeNum = nums.length - 1 while (nums[exChangeNum] <= nums[descrNum]) exChangeNum-- ;[nums[descrNum], nums[exChangeNum]] = [nums[exChangeNum], nums[descrNum]] } // 排序下降点之后的元素, 构成更小的排列 let r = nums.length - 1 descrNum++ while (descrNum < r) { ;[nums[descrNum++], nums[r--]] = [nums[r], nums[descrNum]] } }
寻找重复数
1 2 3 4 5 6 7 8
functionfindDuplicate(nums: number[]): number { const hashSet = newSet<number>() for (const num of nums) { if (hashSet.has(num)) return num hashSet.add(num) } return -1 }