Part I 哈希表
1. 两数之和
python">class Solution(object):def twoSum(self, nums, target):""":type nums: List[int]:type target: int:rtype: List[int]"""if not nums:return d = {}for i, n in enumerate(nums):k = target - nif k not in d:d[n] = ielse:return [i, d[k]]
2. 字母异位词分组
考察字符串的编码
ord函数:返回ascii码值
python">class Solution(object):def groupAnagrams(self, strs):""":type strs: List[str]:rtype: List[List[str]]"""def encode(s):code = [0] * 26for c in s:idx = ord(c) - ord('a')code[idx] += 1return ",".join([str(n) for n in code])d = {}for s in strs:x = encode(s)if x not in d:d[x] = [s]else:d[x].append(s)return d.values()
3. 最长连续序列
- HashSet的运用
python">class Solution(object):def longestConsecutive(self, nums):""":type nums: List[int]:rtype: int"""if not nums:return 0nums_set = set(nums)res = 1temp = 1for n in nums:if n - 1 in nums_set:# 找到连续序列的起点continue else:temp = 1while n + 1 in nums_set:n += 1temp += 1res = max(res, temp)return res
Part II 双指针
4. 移动零
快慢指针
python">class Solution(object):def moveZeroes(self, nums):""":type nums: List[int]:rtype: None Do not return anything, modify nums in-place instead."""slow, fast = 0, 0n = len(nums)while fast < n:if nums[fast] != 0:nums[slow], nums[fast] = nums[fast], nums[slow]slow += 1fast += 1return nums
5. 成水最多的容器
双指针,贪心。移动更短的柱子
python">class Solution(object):def maxArea(self, height):""":type height: List[int]:rtype: int"""n = len(height)if n <= 1:return 0left, right = 0, n-1res = 0cur = 0while left < right:cur = (right - left) * min(height[right], height[left])res = max(res, cur)if height[right] < height[left]:right -= 1else:left += 1return res
6. 三数之和
- 先排序
- 去重:相同的元素跳过(continue)
- 拆解成twoSum
python">class Solution(object):def threeSum(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""def twoSum(nums, start, end, target):res2 = []lo, hi = start, endwhile lo < hi: s = nums[lo] + nums[hi]if s > target:hi -= 1elif s < target:lo += 1else:res2.append([nums[lo], nums[hi]])lo += 1hi -= 1# 跳过重复元素while lo < hi and nums[lo] == nums[lo - 1]:lo += 1while lo < hi and nums[hi] == nums[hi + 1]:hi -= 1return res2if not nums:return nums.sort()res = []for i, n in enumerate(nums):if i > 0 and nums[i-1] == nums[i]:continueif n > 0:breaktemp = twoSum(nums, i+1, len(nums)-1, -n)for item in temp:res.append([n, item[0], item[1]]) return res
7. 接雨水
- 备忘录优化,O(n), O(n)
- 双指针往中间扩散,移动更短的柱子O(n), O(1)
python">class Solution(object):def trap(self, height):""":type height: List[int]:rtype: int"""if not height:return 0lmax, rmax = 0, 0lo, hi = 0, len(height)-1res = 0while lo <= hi:lmax = max(lmax, height[lo])rmax = max(rmax, height[hi])if lmax <= rmax:res += max(lmax-height[lo], 0)lo += 1else:res += max(rmax-height[hi], 0)hi -= 1return res
Part III 滑动窗口
8. 无重复字符的最长子串
滑动窗口标准解法
python">class Solution(object):def lengthOfLongestSubstring(self, s):""":type s: str:rtype: int"""if not s:return 0n = len(s)lo, hi = 0, 0d = {}res = 0while hi < n:ch = s[hi]d[ch] = d.get(ch, 0) + 1while d.get(ch, 0) > 1:d[s[lo]] -= 1lo += 1hi += 1res = max(res, hi-lo)return res
9. 找到字符串中的所有字母异位词
python">class Solution(object):def findAnagrams(self, s, p):""":type s: str:type p: str:rtype: List[int]"""if not s:return need = {}for c in p:need[c] = need.get(c, 0) + 1window = {}lo, hi = 0, 0n = len(s)res = []valid = 0while hi < n:c = s[hi]# hi += 1if c in need:window[c] = window.get(c, 0) + 1if window[c] == need[c]:valid += 1while hi - lo + 1 >= len(p):if valid == len(need):res.append(lo)d = s[lo]lo += 1if d in window and window[d] > 0: if window[d] == need[d]:valid -= 1window[d] -= 1hi += 1return res
Part IV 子串
10. 和为k的子数组
前缀和,参考https://labuladong.online/algo/ds-class/shu-zu-lia-39fd9/jing-dian–52d44/
python">class Solution(object):def subarraySum(self, nums, k):""":type nums: List[int]:type k: int:rtype: int"""if not nums:return 0n = len(nums)preSum = [0 for _ in range(n+1)]for i in range(1, n+1):preSum[i] = preSum[i-1] + nums[i-1]val2idx = {}res = 0for i in range(0, n+1): if preSum[i]-k in val2idx:res += val2idx[preSum[i] - k] val2idx[preSum[i]] = val2idx.get(preSum[i], 0) + 1 return res
11. 滑动窗口最大值
https://labuladong.online/algo/data-structure/monotonic-queue/
单调队列,加入数字的大小代表人的体重,体重大的会把前面体重不足的压扁,直到遇到更大的量级才停住。
python">class MonotonicQueue:def __init__(self):self.maxq = deque()def push(self, n):while len(self.maxq) > 0 and self.maxq[-1] < n:self.maxq.pop()self.maxq.append(n)def pop(self, n):if n == self.maxq[0]:self.maxq.popleft()def max(self):return self.maxq[0]
class Solution(object):def maxSlidingWindow(self, nums, k):""":type nums: List[int]:type k: int:rtype: List[int]"""if not nums:return mq = MonotonicQueue()for i in range(k):mq.push(nums[i]) res = [mq.max()]lo, hi = 0, k-1 while hi < len(nums)-1:hi += 1mq.push(nums[hi])mq.pop(nums[lo])lo += 1res.append(mq.max()) return res
12. 最小覆盖子串
滑动窗口解法
python">class Solution(object):def minWindow(self, s, t):""":type s: str:type t: str:rtype: str"""n, k = len(s), len(t)if n < k:return ""lo, hi = 0, 0valid = 0need, window = {}, {}for c in t:need[c] = need.get(c, 0) + 1res = ""while hi < n:c = s[hi]if c in need:window[c] = window.get(c, 0) + 1if window[c] == need[c]:valid += 1hi += 1while valid == len(need): if (not res) or len(res) > hi-lo:res = s[lo:hi]c = s[lo]if c in window:if window[c] == need[c]:valid -= 1window[c] -= 1lo += 1 return res
Part V 普通数组
13. 最大子数组和
https://leetcode.cn/problems/maximum-subarray/?envType=study-plan-v2&envId=top-100-liked
求最值 -> 第一时间想到动态规划,不要被滑动窗口带偏了
O(n), O(1)
python">class Solution(object):def maxSubArray(self, nums):""":type nums: List[int]:rtype: int"""if not nums:return 0n = len(nums)# d = [0 for _ in range(n)]d0 = nums[0]# d1 = 0res = d0for i in range(1, n):d1 = max(d0+nums[i], nums[i])d0 = d1res = max(res, d1)return res
14. 合并区间
https://leetcode.cn/problems/merge-intervals/?envType=study-plan-v2&envId=top-100-liked
区间问题合集:https://labuladong.online/algo/practice-in-action/interval-problem-summary/
- 排序,只对start排序即可,end无所谓
- 难点:找到res中最后一个区间last,在循环中更新last
python">class Solution(object):def merge(self, intervals):""":type intervals: List[List[int]]:rtype: List[List[int]]"""if not intervals:return intervals.sort(key = lambda x : (x[0]))res = [intervals[0]]for i in range(1, len(intervals)):last = res[-1]lo, hi = last[0], last[1]intv = intervals[i] if intv[1] <= hi:passelif intv[0] <= hi and intv[1] > hi:res[-1] = [lo, intv[1]]else:res.append(intv)return res
15 轮转数组
O(n)解法:
python">class Solution(object):def rotate(self, nums, k):""":type nums: List[int]:type k: int:rtype: None Do not return anything, modify nums in-place instead."""n = len(nums)if not n:return temp = nums[:]for i in range(n):nums[(i+k)%n] = temp[i]
O(1): 翻转数组,比较难想;需要手动实现一个reverse函数
python">class Solution(object):def rotate(self, nums, k):""":type nums: List[int]:type k: int:rtype: None Do not return anything, modify nums in-place instead."""def reverse(nums, i, j):while i < j:nums[i], nums[j] = nums[j], nums[i]i += 1j -= 1n = len(nums)k = k%nreverse(nums, 0, n-1)reverse(nums, 0, k-1)reverse(nums, k, n-1)return nums
16 除自身以外数组的乘积
https://leetcode.cn/problems/product-of-array-except-self/description/?envType=study-plan-v2&envId=top-100-liked
和前缀和类似,新建两个数组L和R,记录左边和右边所有元素的乘积。
O(n), O(n)
空间O(1)的技巧有点trick,先不看了
python">class Solution(object):def productExceptSelf(self, nums):""":type nums: List[int]:rtype: List[int]"""n = len(nums)L, R = [1] * n , [1] * nfor i in range(1, n):L[i] = L[i-1] * nums[i-1]for i in range(n-2, -1, -1):R[i] = R[i+1] * nums[i+1]res = [0] * nfor i in range(n):res[i] = L[i] * R[i]return res
17. 缺失的第一个正数
https://leetcode.cn/problems/first-missing-positive/description/?envType=study-plan-v2&envId=top-100-liked
思路:将nums中所有数字放入哈希表,然后遍历1到n+1.
O(n),O(n).
空间O(1)的方法同样很trick了,感觉面试用不到先过
python">class Solution(object):def firstMissingPositive(self, nums):""":type nums: List[int]:rtype: int"""d = {}for n in nums:if n > 0 and n not in d:d[n] = 1for i in range(1, len(nums)+2):print(i)if i not in d:return i
矩阵
18. 矩阵置零
https://leetcode.cn/problems/set-matrix-zeroes/description/?envType=study-plan-v2&envId=top-100-liked
O(mn), O(m+n)
python">class Solution(object):def setZeroes(self, matrix):""":type matrix: List[List[int]]:rtype: None Do not return anything, modify matrix in-place instead."""if not matrix:returnm, n = len(matrix), len(matrix[0])rows = [1 for i in range(m)]cols = [1 for i in range(n)]for i in range(m):for j in range(n):if matrix[i][j] == 0:rows[i] = 0cols[j] = 0for i in range(m):for j in range(n):matrix[i][j] *= rows[i]*cols[j]return matrix
O(1)方法:用矩阵的第一行和第一列代替rows和cols. 但要用额外的两个flag记录第一行和第一列是否有0. 感觉可读性变差很多,忽略。
19. 螺旋矩阵
https://leetcode.cn/problems/spiral-matrix/description/?envType=study-plan-v2&envId=top-100-liked
参考:二维数组的遍历技巧 https://labuladong.online/algo/practice-in-action/2d-array-traversal-summary/#%E7%9F%A9%E9%98%B5%E7%9A%84%E8%9E%BA%E6%97%8B%E9%81%8D%E5%8E%86
解题的核心思路是按照右、下、左、上的顺序遍历数组,并使用四个变量圈定未遍历元素的边界
python">class Solution(object):def spiralOrder(self, matrix):""":type matrix: List[List[int]]:rtype: List[int]"""if not matrix:returnm, n = len(matrix), len(matrix[0])upper_bound, lower_bound, left_bound, right_bound = 0, m-1, 0, n-1res = []while len(res) < m*n:if upper_bound <= lower_bound:for j in range(left_bound, right_bound+1):res.append(matrix[upper_bound][j])upper_bound += 1if right_bound >= left_bound:for i in range(upper_bound, lower_bound + 1):res.append(matrix[i][right_bound])right_bound -= 1if upper_bound <= lower_bound:for j in range(right_bound, left_bound-1, -1):res.append(matrix[lower_bound][j])lower_bound -= 1if right_bound >= left_bound:for i in range(lower_bound, upper_bound-1, -1):res.append(matrix[i][left_bound])left_bound += 1return res
20 旋转二维图像
先按照左上->右下对角线翻转,再逐行翻转
python">class Solution(object):def rotate(self, matrix):""":type matrix: List[List[int]]:rtype: None Do not return anything, modify matrix in-place instead."""if not matrix:returnn = len(matrix)for i in range(n):for j in range(i+1, n):matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]for i in matrix:i.reverse()return matrix
21 搜索二维数组
https://leetcode.cn/problems/search-a-2d-matrix-ii/description/?envType=study-plan-v2&envId=top-100-liked
二分搜索思想,从右上角开始。因为往左是变小,往下是变大,所以按照这个方向搜索即可。
python">class Solution(object):def searchMatrix(self, matrix, target):""":type matrix: List[List[int]]:type target: int:rtype: bool"""if not matrix:return Falsem, n = len(matrix), len(matrix[0])i, j = 0, n-1while i <= m-1 and j >= 0:if target == matrix[i][j]:return Trueelif target > matrix[i][j]:i += 1else:j -= 1return False
链表
22. 相交链表
https://leetcode.cn/problems/intersection-of-two-linked-lists/?envType=study-plan-v2&envId=top-100-liked
这道题不难,思路也都能想到。但具体实现的时候 while n1 != n2这个循环条件想了比较久。一开始想的是 while True,但没有跳出循环的条件。仔细想了下,n1和n2要么相交,要么都走向None,最终都是n1 == n2.
python">class Solution(object):def getIntersectionNode(self, headA, headB):""":type head1, head1: ListNode:rtype: ListNode"""n1, n2 = headA, headBwhile n1 != n2:if not n1:n1 = headBelse:n1 = n1.nextif not n2:n2 = headAelse:n2 = n2.nextreturn n1
23 反转链表
这道题的迭代解法必须熟练掌握,面试的时候保证自己5分钟内bug free写出来。因为后续很多题目会用到这个函数,还包括一些扩展,比如反转a、b两个结点之间的链表等等。
https://leetcode.cn/problems/reverse-linked-list/description/?envType=study-plan-v2&envId=top-100-liked
递归:O(n), O(n)
reverseList的功能:给定node,返回反转后node的头结点
因此需要做两件事:把head.next的next指向自己;把head.next断掉
python">class Solution(object):def reverseList(self, head):""":type head: ListNode:rtype: ListNode"""if not head or not head.next:return headlast = self.reverseList(head.next)head.next.next = head head.next = Nonereturn last
迭代:O(n), O(1)
python">class Solution(object):def reverseList(self, head):""":type head: ListNode:rtype: ListNode"""prev = Nonecur = head while cur:next = cur.nextcur.next = prevprev = curcur = nextreturn prev
24.回文链表
https://leetcode.cn/problems/palindrome-linked-list/description/?envType=study-plan-v2&envId=top-100-liked
存到数组里,O(n), O(n).
python">class Solution(object):def isPalindrome(self, head):""":type head: ListNode:rtype: bool"""if not head:return node = headnode2list = []while node:node2list.append(node.val)node = node.nextleft, right = 0, len(node2list)-1while left < right:if node2list[left] != node2list[right]:return Falseleft += 1right -= 1return True
空间O(1):先找到中点,再用迭代法反转后半段链表,然后再双指针判断。太麻烦了
python"># Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):def isPalindrome(self, head):""":type head: ListNode:rtype: bool"""def reverse(node):if not node or not node.next:return nodeprev = Nonecur = nodewhile cur:# print("CUR:", cur)nxt = cur.nextcur.next = prevprev = curcur = nxtreturn previf not head or not head.next:return Trueslow, fast = head, head.nextwhile fast and fast.next:slow = slow.nextfast = fast.next.next# print(slow.val)slow.next = reverse(slow.next)p1, p2 = head, slow.nextwhile p1 and p2:if p1.val != p2.val:return Falsep1 = p1.nextp2 = p2.nextreturn True
25 环形链表 && ## 26 环形链表II
做了几百遍了略过
27 合并两个有序链表
直接双指针
python">class Solution(object):def mergeTwoLists(self, list1, list2):""":type list1: Optional[ListNode]:type list2: Optional[ListNode]:rtype: Optional[ListNode]"""n1 = list1n2 = list2dummy = ListNode(0)cur = dummywhile n1 and n2:if n1.val < n2.val:cur.next = n1n1 = n1.nextelse:cur.next = n2n2 = n2.nextcur = cur.nextif n1:cur.next = n1if n2:cur.next = n2return dummy.next
28 两数之和,链表版
https://leetcode.cn/problems/add-two-numbers/?envType=study-plan-v2&envId=top-100-liked
- 不要想着转化成数字相加,会溢出
- 注意while条件,carry要加上。否则最高一位进位后会丢数据
python">class Solution(object):def addTwoNumbers(self, l1, l2):""":type l1: ListNode:type l2: ListNode:rtype: ListNode""" n1, n2 = l1, l2cur = 0carry = 0dummy = ListNode(0)res = dummywhile n1 or n2 or carry>0:cur = carryif n1:cur += n1.valn1 = n1.nextif n2:cur += n2.valn2 = n2.next if cur >= 10:carry = 1cur = cur%10 else:carry = 0res.next = ListNode(cur)res = res.next return dummy.next
29 删除链表的倒数第n个节点
双指针。注意n=sz的情况,用dummy处理虚拟头结点
python">class Solution(object):def removeNthFromEnd(self, head, n):""":type head: ListNode:type n: int:rtype: ListNode"""if not head:returnp1, p2 = head, headfor i in range(n): p2 = p2.nextdummy = ListNode(0)dummy.next = headprev = dummywhile p2:prev = p1p1 = p1.nextp2 = p2.nextprev.next = p1.nextp1.next = Nonereturn dummy.next
30. 两两交换链表中的结点
迭代法,虚拟头结点。O(n), O(1)
python">class Solution(object):def swapPairs(self, head):""":type head: ListNode:rtype: ListNode"""if not head:returndummy = ListNode(0)dummy.next = headp1, p2 = head, head.nextprev = dummywhile p2:prev.next = p2nxt = p2.nextp2.next = p1p1.next = nxtp1, p2 = p2, p1prev = p2p1 = p1.next.nextif p2.next and p2.next.next:p2 = p2.next.nextelse:breakreturn dummy.next
递归,O(n),O(n)
递归函数定义:给定头结点n,两两反转链表,然后返回头结点。好清晰
python">class Solution(object):def swapPairs(self, head):""":type head: ListNode:rtype: ListNode"""if not head or not head.next:return headp1, p2, p3 = head, head.next, head.next.nextp1.next = p2p2.next = p1p1.next = self.swapPairs(p3)return p2
31. k个一组反转链表
主要考察迭代法反转链表,以及递归的理解。
- 反转[a, b)之间的的链表
- 递归
python">class Solution(object):def reverseKGroup(self, head, k):""":type head: ListNode:type k: int:rtype: ListNode"""def reverse(p1, p2):if not p1 or not p1.next or p1 == p2 or p1.next == p2:return p1cur = p1prev = Nonewhile cur!= p2:nxt = cur.nextcur.next = prevprev = curcur = nxtreturn previf not head or not head.next:return headp1, p2 = head, headfor i in range(k):if p2:p2 = p2.nextelse:return p1# 下面这几句是精华,用newHead记录反转后的头结点。此时p1是反转后的尾结点,p2是剩余链表的头结点。newHead = reverse(p1, p2)p1.next = self.reverseKGroup(p2, k)return newHead
32 随机链表的拷贝
https://leetcode.cn/problems/copy-list-with-random-pointer/description/?envType=study-plan-v2&envId=top-100-liked
难者不会会者不难。数据结构的拷贝:哈希表+两次遍历
第一次遍历,克隆结点,用哈希表把原结点和映射后结点对应存储起来;第二次遍历,组装结点,把next、random组装起来
python">class Solution(object):def copyRandomList(self, head):""":type head: Node:rtype: Node"""if not head:returnnode2dict = {}p = headwhile p: node2dict[p] = Node(p.val)p = p.nextp = headwhile p:if p.next:node2dict[p].next = node2dict[p.next]if p.random:node2dict[p].random = node2dict[p.random]p = p.nextreturn node2dict[head]
33. 链表排序
https://leetcode.cn/problems/sort-list/?envType=study-plan-v2&envId=top-100-liked
- 首先复习一下归并排序 https://labuladong.online/algo/practice-in-action/merge-sort/
- 和数组相比,链表不用额外temp数组,会更方便
- 先找到链表中点,再分割,然后递归
python">class Solution(object):def sortList(self, head):""":type head: ListNode:rtype: ListNode"""def mergeSort(head):if not head or not head.next:return headmid = findMiddle(head)nxt = mid.nextmid.next = None p1 = mergeSort(head)p2 = mergeSort(nxt) return merge(p1, p2)def findMiddle(head):slow, fast = head, head.nextwhile fast and fast.next:slow = slow.nextfast = fast.next.nextreturn slowdef merge(p1, p2):dummy = ListNode(0)cur = dummywhile p1 or p2:if not p1:cur.next = p2p2 = Noneelif not p2:cur.next = p1p1 = Noneelif p1.val < p2.val:cur.next = p1p1 = p1.nextelse:cur.next = p2p2 = p2.nextcur = cur.nextreturn dummy.nextreturn mergeSort(head)
34 合并k个升序链表
堆(优先队列),O(nlogk). k是优先队列的元素个数,一次push或者pop的时间复杂度为logk
python">class Solution(object):def mergeKLists(self, lists):""":type lists: List[ListNode]:rtype: ListNode"""import heapqpq = []for n in lists:if n:heapq.heappush(pq, (n.val, n))dummy = ListNode(0)cur = dummywhile pq:n = heapq.heappop(pq)[1]nxt = n.nextn.next = Nonecur.next = ncur = cur.nextif nxt:heapq.heappush(pq, (nxt.val, nxt))return dummy.next
35 LRU算法
数据结构:哈希链表。
- 先新建出Node和DoubleLinkedList的类,并写好初始化函数
- 从get和put逻辑开始倒推写。
python">class Node(object):def __init__(self, key, val):self.key = keyself.val = valself.next = Noneself.prev = None
class DoubleLinkedList(object):def __init__(self):self.head = Node(0, 0)self.tail = Node(0, 0)self.head.next = self.tailself.tail.prev = self.headself.size = 0def addLast(self, node):prv = self.tail.prevprv.next = nodenode.prev = prvnode.next = self.tailself.tail.prev = nodeself.size += 1def remove(self, node):if not node or not node.next or not node.prev:returnprv = node.prevnxt = node.nextprv.next = nxtnxt.prev = prvself.size -= 1def removeFirst(self):if self.size == 0:returnnode = self.head.nextnxt = node.nextself.head.next = nxtnxt.prev = self.headself.size -= 1return nodeclass LRUCache(object):def __init__(self, capacity):""":type capacity: int"""self.capacity = capacityself.hashMap = {}self.cache = DoubleLinkedList()def makeRecently(self, key): node = self.hashMap[key]self.cache.remove(node)self.cache.addLast(node)def addRecently(self, key, val):node = Node(key, val)self.cache.addLast(node)self.hashMap[key] = nodedef deleteKey(self, key):if key not in self.hashMap:return Nonenode = self.hashMap[key]self.cache.remove(node)del self.hashMap[key]def removeLeastRecetly(self):node = self.cache.removeFirst() del self.hashMap[node.key] def get(self, key):""":type key: int:rtype: int"""if key not in self.hashMap:return -1else: res = self.hashMap[key]self.makeRecently(key)return res.valdef put(self, key, value):""":type key: int:type value: int:rtype: None"""if key in self.hashMap:node = self.hashMap[key]node.val = valueself.makeRecently(key)else:if self.cache.size >= self.capacity:self.removeLeastRecetly()self.addRecently(key, value)