剑指 Offer
跨国巨头公司面试官押题宝典。
题目
字符串
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 05. 替换空格 | 简单 | 已解答 | 字符串 |
剑指 Offer 58 - II. 左旋转字符串 | 简单 | 已解答 | 字符串、数学、双指针 |
剑指 Offer 20. 表示数值的字符串 | 中等 | 已解答 | 字符串 |
剑指 Offer 67. 把字符串转换成整数 | 中等 | 已解答 | 字符串 |
链表
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 06. 从尾到头打印链表 | 简单 | 已解答 | 栈、递归、链表 |
剑指 Offer 24. 反转链表 | 简单 | 已解答 | 递归、链表 |
剑指 Offer 35. 复杂链表的复制 | 中等 | 已解答 | 哈希表、链表 |
双指针
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 18. 删除链表的节点 | 简单 | 已解答 | 链表 |
剑指 Offer 22. 链表中倒数第k个节点 | 简单 | 已解答 | 链表、双指针 |
剑指 Offer 25. 合并两个排序的链表 | 简单 | 已解答 | 递归、链表 |
剑指 Offer 52. 两个链表的第一个公共节点 | 简单 | 已解答 | 递归、链表 |
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 | 简单 | 已解答 | 数组、双指针、排序 |
剑指 Offer 57. 和为s的两个数字 | 简单 | 已解答 | 数组、双指针、二分查找 |
剑指 Offer 58 - I. 翻转单词顺序 | 简单 | 已解答 | 双指针、字符串 |
栈与队列
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 09. 用两个栈实现队列 | 简单 | 已解答 | 栈、设计、队列 |
剑指 Offer 30. 包含min函数的栈 | 简单 | 已解答 | 栈、设计 |
剑指 Offer 59 - I. 滑动窗口的最大值 | 困难 | 已解答 | 队列、滑动窗口、单调队列 |
剑指 Offer 59 - II. 队列的最大值 | 中等 | 已解答 | 设计、队列、单调队列 |
模拟
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 29. 顺时针打印矩阵 | 简单 | 已解答 | 数组、矩阵、模拟 |
剑指 Offer 31. 栈的压入、弹出序列 | 中等 | 已解答 | 栈、数组、模拟 |
查找算法
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 03. 数组中重复的数字 | 简单 | 已解答 | 数组、哈希表、排序 |
剑指 Offer 53 - I. 在排序数组中查找数字 I | 简单 | 已解答 | 数组、二分查找 |
剑指 Offer 53 - II. 0~n-1中缺失的数字 | 简单 | 已解答 | 位运算、数组、哈希表 |
剑指 Offer 04. 二维数组中的查找 | 中等 | 已解答 | 数组、二分查找、分治 |
剑指 Offer 11. 旋转数组的最小数字 | 简单 | 已解答 | 数组、二分查找 |
剑指 Offer 50. 第一个只出现一次的字符 | 简单 | 已解答 | 队列、哈希表、字符串 |
搜索与回溯算法
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 32 - I. 从上到下打印二叉树 | 中等 | 已解答 | 树、广度优先搜索、二叉树 |
剑指 Offer 32 - II. 从上到下打印二叉树 II | 简单 | 已解答 | 树、广度优先搜索、二叉树 |
剑指 Offer 32 - III. 从上到下打印二叉树 III | 中等 | 已解答 | 树、广度优先搜索、二叉树 |
剑指 Offer 26. 树的子结构 | 中等 | 已解答 | 广度优先搜索、二叉树 |
剑指 Offer 27. 二叉树的镜像 | 简单 | 已解答 | 树、深度优先搜索、二叉树 |
剑指 Offer 28. 对称的二叉树 | 简单 | 已解答 | 树、深度优先搜索、广度优先搜索 |
剑指 Offer 12. 矩阵中的路径 | 中等 | 已解答 | 数组、回溯、矩阵 |
剑指 Offer 13. 机器人的运动范围 | 中等 | 已解答 | 深度优先搜索、广度优先搜索、动态规划 |
剑指 Offer 34. 二叉树中和为某一值的路径 | 中等 | 已解答 | 树、深度优先搜索、回溯 |
剑指 Offer 36. 二叉搜索树与双向链表 | 中等 | 已解答 | 栈、树、深度优先搜索 |
剑指 Offer 54. 二叉搜索树的第k大节点 | 简单 | 已解答 | 树、深度优先搜索、二叉搜索树 |
剑指 Offer 55 - I. 二叉树的深度 | 简单 | 已解答 | 树、深度优先搜索、广度优先搜索 |
剑指 Offer 55 - II. 平衡二叉树 | 简单 | 已解答 | 树、深度优先搜索、二叉树 |
剑指 Offer 64. 求1+2+…+n | 中等 | 已解答 | 位运算、递归、脑筋急转弯 |
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 | 简单 | 已解答 | 树、深度优先搜索、二叉搜索树 |
剑指 Offer 68 - II. 二叉树的最近公共祖先 | 简单 | 已解答 | 树、深度优先搜索、二树 |
剑指 Offer 37. 序列化二叉树 | 困难 | 已解答 | 树、深度优先搜索、广度优先搜索 |
剑指 Offer 38. 字符串的排列 | 中等 | 已解答 | 字符串、回溯 |
分治算法
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 07. 重建二叉树 | 中等 | 已解答 | 树、数组、哈希表 |
剑指 Offer 16. 数值的整数次方 | 中等 | 已解答 | 递归、数学 |
剑指 Offer 33. 二叉搜索树的后序遍历序列 | 中等 | 已解答 | 栈、数、二叉搜素树 |
剑指 Offer 17. 打印从1到最大的n位数 | 简单 | 已解答 | 数组、数学 |
剑指 Offer 51. 数组中的逆序对 | 困难 | 已解答 | 树状数组、线段树、数组 |
排序
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 45. 把数组排成最小的数 | 中等 | 已解答 | 贪心、字符串、排序 |
剑指 Offer 61. 扑克牌中的顺子 | 简单 | 已解答 | 数组、排序 |
剑指 Offer 40. 最小的k个数 | 简单 | 已解答 | 数组、分治、快速选择 |
剑指 Offer 41. 数据流中的中位数 | 困难 | 已解答 | 设计、双指针、数据流 |
动态规划
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 10- I. 斐波那契数列 | 简单 | 已解答 | 记忆化搜索、数学、动态规划 |
剑指 Offer 10- II. 青蛙跳台阶问题 | 简单 | 已解答 | 记忆化搜索、数学、动态规划 |
剑指 Offer 63. 股票的最大利润 | 中等 | 已解答 | 数组、动态规划 |
剑指 Offer 42. 连续子数组的最大和 | 简单 | 已解答 | 数组、分治、动态规划 |
剑指 Offer 47. 礼物的最大价值 | 中等 | 已解答 | 数组、动态规划、矩阵 |
剑指 Offer 46. 把数字翻译成字符串 | 中等 | 已解答 | 字符串、动态规划 |
剑指 Offer 48. 最长不含重复字符的子字符串 | 中等 | 已解答 | 哈希表、字符串、滑动窗口 |
剑指 Offer 19. 正则表达式匹配 | 困难 | 已解答 | 递归、字符串、动态规划 |
剑指 Offer 49. 丑数 | 中等 | 已解答 | 哈希表、数学、动态规划 |
剑指 Offer 60. n个骰子的点数 | 中等 | 已解答 | 数学、动态规划、概率与统计 |
位运算
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 15. 二进制中1的个数 | 简单 | 已解答 | 位运算 |
剑指 Offer 65. 不用加减乘除做加法 | 简单 | 已解答 | 位运算、数学 |
剑指 Offer 56 - I. 数组中数字出现的次数 | 中等 | 已解答 | 位运算、数组 |
剑指 Offer 56 - II. 数组中数字出现的次数 II | 中等 | 已解答 | 位运算、数组 |
数学
名称 | 级别 | 状态 | 备注 |
---|---|---|---|
剑指 Offer 39. 数组中出现次数超过一半的数字 | 简单 | 已解答 | 数组、哈希表、分治 |
剑指 Offer 66. 构建乘积数组 | 中等 | 已解答 | 数组、前缀和 |
剑指 Offer 14- I. 剪绳子 | 中等 | 已解答 | 数学、动态规划 |
剑指 Offer 14- II. 剪绳子 II | 中等 | 已解答 | 数学、动态规划 |
剑指 Offer 57 - II. 和为s的连续正数序列 | 简单 | 已解答 | 数学、双指针、枚举 |
剑指 Offer 62. 圆圈中最后剩下的数字 | 简单 | 已解答 | 递归、数学 |
剑指 Offer 43. 1~n 整数中 1 出现的次数 | 困难 | 已解答 | 递归、数学、动态规划 |
剑指 Offer 44. 数字序列中某一位的数字 | 中等 | 已解答 | 数学、二分查找 |
解题
字符串
剑指 Offer 05. 替换空格
class Solution:
def replaceSpace(self, s: str) -> str:
if not s: return ""
# 时间复杂度:O(N)
# 空间复杂度:O(N)
result = ""
for i in s:
if i == ' ':
result += '%20'
else:
result += i
return result
剑指 Offer 58 - II. 左旋转字符串
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
if not s and k < 1: return ""
# py 下标获取字符串是左闭,右开
return s[n:len(s)] + s[0:n]
剑指 Offer 20. 表示数值的字符串
class Solution:
def isNumber(self, s: str) -> bool:
try:
float(s.strip())
except:
return False
return True
剑指 Offer 67. 把字符串转换成整数
class Solution:
def strToInt(self, str: str) -> int:
# 去除前导空格
str = str.strip()
if not str: return 0
if len(str) == 0: return 0
sign = 1
if str[0] == '-':
sign = -1
str = str[1:]
elif str[0] == '+':
str = str[1:]
# 转换字符串为整数
res = 0
for c in str:
if not c.isdigit():
break
res = res * 10 + int(c)
# 判断是否超过整数范围
if res > 2**31 - 1:
return 2**31 - 1 if sign == 1 else -2**31
return sign * res
链表
剑指 Offer 06. 从尾到头打印链表
class Solution:
def reversePrint(self, head: ListNode) -> List[int]:
res = []
curr = head
while curr:
res.append(curr.val)
curr = curr.next
return res[::-1]
剑指 Offer 24. 反转链表
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if not head: return None
prev = None
curr = head
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
return prev
剑指 Offer 35. 复杂链表的复制
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
self.map = {}
return self.copList(head)
def copList(self, head):
if not head: return None
if head in self.map:
return self.map.get(head)
else:
node = Node(head.val)
self.map[head] = node
node.next = self.copList(head.next)
node.random = self.copList(head.random)
return self.map.get(head)
双指针
剑指 Offer 18. 删除链表的节点
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
if not head: return None
if head.val == val: return head.next
pre, cur = head, head.next
while cur:
if cur.val == val:
pre.next = cur.next
break
else:
pre, cur = cur, cur.next
return head
剑指 Offer 22. 链表中倒数第k个节点
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
fast = head
low = head
for _ in range(k):
if not fast: return
fast = fast.next
while fast:
fast,low = fast.next, low.next
return low
剑指 Offer 25. 合并两个排序的链表
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
if l1 is None:
return l2
elif l2 is None:
return l1
elif l1.val < l2.val:
l1.next = self.mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2
剑指 Offer 52. 两个链表的第一个公共节点
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
l1 = headA
l2 = headB
while l1 != l2:
if l1 is None:
l1 = headB
else:
l1 = l1.next
if l2 is None:
l2 = headA
else:
l2 = l2.next
return l1
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
class Solution:
def exchange(self, nums: List[int]) -> List[int]:
if not nums: return nums
return self.s2(nums)
def s2(self, nums):
n = len(nums)
res,left,right = [0] * n,0,n-1
for num in nums:
if num % 2 == 1:
res[left] = num
left +=1
else:
res[right] = num
right -= 1
return res
def s1(self, nums):
res = []
for i in nums:
if i%2 == 1:
res.append(i)
for i in nums:
if i%2 == 0:
res.append(i)
return res
剑指 Offer 57. 和为s的两个数字
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
if not nums: return []
left,right = 0,len(nums)-1
while left <= right:
if nums[left] + nums[right] == target:
return [nums[left],nums[right]]
elif nums[left] + nums[right] > target:
right -=1
else:
left +=1
return []
剑指 Offer 58 - I. 翻转单词顺序
class Solution:
def reverseWords(self, s: str) -> str:
if not s: return ""
res = ""
words = s.strip().split(" ")
for i in range(len(words)-1, -1, -1):
if len(words[i]) > 0:
res += " " + words[i]
return res.strip()
栈与队列
剑指 Offer 09. 用两个栈实现队列
class CQueue:
def __init__(self):
self.in_s = []
self.out_s = []
def appendTail(self, value: int) -> None:
self.in_s.append(value)
def deleteHead(self) -> int:
if len(self.out_s) == 0:
if len(self.in_s) == 0:
return -1
else:
while self.in_s:
self.out_s.append(self.in_s.pop())
return self.out_s.pop()
# Your CQueue object will be instantiated and called as such:
# obj = CQueue()
# obj.appendTail(value)
# param_2 = obj.deleteHead()
剑指 Offer 30. 包含min函数的栈
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
self.min_stack = []
def push(self, x: int) -> None:
self.stack.append(x)
if len(self.min_stack) == 0 or x <= self.min_stack[-1]:
self.min_stack.append(x)
def pop(self) -> None:
r = self.stack.pop()
if r == self.min_stack[-1]:
self.min_stack.pop()
def top(self) -> int:
return self.stack[-1]
def min(self) -> int:
return self.min_stack[-1]
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.min()
剑指 Offer 59 - I. 滑动窗口的最大值
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
n = len(nums)
q = [(-nums[i], i) for i in range(k)]
heapq.heapify(q)
ans = [-q[0][0]]
for i in range(k, n):
heapq.heappush(q, (-nums[i], i))
while q[0][1] <= i-k:
heapq.heappop(q)
ans.append(-q[0][0])
return ans
def s1(self, nums, k):
start,end = 0,k
res = []
while end <= len(nums):
v = self.maxWindow(nums, start, end)
res.append(v)
start += 1
end += 1
return res
def maxWindow(self, nums, start, end):
maximun = float("-inf")
for i in range(start, end):
if maximun < nums[i]:
maximun = nums[i]
return maximun
剑指 Offer 59 - II. 队列的最大值
class MaxQueue:
def __init__(self):
self.q = deque()
def max_value(self) -> int:
return max(self.q) if self.q else -1
def push_back(self, value: int) -> None:
self.q.append(value)
def pop_front(self) -> int:
return self.q.popleft() if self.q else -1
# Your MaxQueue object will be instantiated and called as such:
# obj = MaxQueue()
# param_1 = obj.max_value()
# obj.push_back(value)
# param_3 = obj.pop_front()
模拟
剑指 Offer 29. 顺时针打印矩阵
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix or not matrix[0]: return list()
rows, columns = len(matrix), len(matrix[0])
order = list()
left, right, top, bottom = 0, columns -1, 0, rows -1
while left <= right and top <= bottom:
for column in range(left, right + 1):
order.append(matrix[top][column])
for row in range(top + 1, bottom + 1):
order.append(matrix[row][right])
if left < right and top < bottom:
for column in range(right-1, left, -1):
order.append(matrix[bottom][column])
for row in range(bottom, top, -1):
order.append(matrix[row][left])
left, right, top, bottom = left + 1, right - 1, top + 1, bottom - 1
return order
剑指 Offer 31. 栈的压入、弹出序列
class Solution:
def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
st, j = [],0
for x in pushed:
st.append(x)
while st and st[-1] == popped[j]:
st.pop()
j += 1
return len(st) == 0
查找算法
剑指 Offer 03. 数组中重复的数字
class Solution:
def findRepeatNumber(self, nums: List[int]) -> int:
if not nums: return -1
numbers = [-1] * len(nums)
for i in nums:
if numbers[i] != -1 and numbers[i] == i:
return i
else:
numbers[i] = i
return -1
剑指 Offer 53 - I. 在排序数组中查找数字 I
class Solution:
def search(self, nums: List[int], target: int) -> int:
return self.helper(nums, target) - self.helper(nums, target - 1)
def helper(self, nums, target):
i,j = 0, len(nums) - 1
while i <= j:
m = (i + j)//2
if nums[m] <= target:
i = m + 1
else:
j = m - 1
return i
剑指 Offer 53 - II. 0~n-1中缺失的数字
class Solution:
def missingNumber(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right)//2
if nums[mid] == mid:
left = mid + 1
else:
right = mid - 1
return left
剑指 Offer 04. 二维数组中的查找
class Solution:
def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
if len(matrix) == 0: return False
for i in range(len(matrix)):
nums = matrix[i]
left,right = 0, len(nums) - 1
while left <= right:
mid = (left + right)//2
if nums[mid] == target:
return True
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return False
剑指 Offer 11. 旋转数组的最小数字
class Solution:
def minArray(self, numbers: List[int]) -> int:
# 只有一个元素,数组的最小值为 1;PS:限制条件
if len(numbers) == 1: return 1
for i in range(1, len(numbers)):
# 旋转以后,子序列为升序,并且前一半序列比后一半都大,如果出现相减 < 0 说明 i 就是我们要找的最小元素
if numbers[i] - numbers[i-1] < 0:
return numbers[i]
# 元素完全相同,比如:[-1,-1,-1] 或者 [1,1,1,1]
return numbers[0]
剑指 Offer 50. 第一个只出现一次的字符
class Solution:
def firstUniqChar(self, s: str) -> str:
if not s: return ' '
dic = dict()
for c in s:
if c in dic:
dic[c] = dic[c] + 1
else:
dic[c] = 1
for k,v in dic.items():
if v == 1: return k
return ' '
def s1(self, s):
if not s: return ' '
kv = dict()
for i in s:
if i in kv:
kv[i] = kv[i] + 1
else:
kv[i] = 1
for i in s:
if i in kv and kv[i] == 1:
return i
return ' '
搜索与回溯算法
剑指 Offer 32 - I. 从上到下打印二叉树
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def levelOrder(self, root: TreeNode) -> List[int]:
# 前序遍历:根左右 -> BFS
if not root: return []
res = []
queue = deque()
queue.append(root)
while queue:
node = queue.popleft()
res.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
return res
剑指 Offer 32 - II. 从上到下打印二叉树 II
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if not root: return []
queue,res = deque(),[]
queue.append(root)
while queue:
tmp = []
for _ in range(len(queue)):
v = queue.popleft()
tmp.append(v.val)
if v.left: queue.append(v.left)
if v.right: queue.append(v.right)
res.append(tmp)
return res
剑指 Offer 32 - III. 从上到下打印二叉树 III
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if not root: return []
# bfs 按层打印,在反转数组
res = []
def bfs(root):
queue = collections.deque()
queue.append(root)
state = True
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if state:
res.append(level)
else:
res.append(level[::-1])
state = False if state else True
bfs(root)
return res
剑指 Offer 26. 树的子结构
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
def recur(A, B):
if not B: return True
if not A or A.val != B.val: return False
return recur(A.left, B.left) and recur(A.right, B.right)
return bool(A and B) and (recur(A, B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B))
剑指 Offer 27. 二叉树的镜像
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def mirrorTree(self, root: TreeNode) -> TreeNode:
# return self.swap(root)
return TreeNode(root.val, self.mirrorTree(root.right), self.mirrorTree(root.left)) if root else None
def swap(self, node):
if node:
self.swap(node.left)
self.swap(node.right)
left = node.left
node.left= node.right
node.right = left
return node
else:
return node
剑指 Offer 28. 对称的二叉树
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
rnode = self.swap(root)
return self.is_same_tree(root, rnode)
def is_same_tree(self, A, B):
# 值都为空,说明相等
if A is None and B is None: return True
# 任意一个为空
if A is None or B is None: return False
# 值不相等
if A.val != B.val: return False
return self.is_same_tree(A.left, B.left) and self.is_same_tree(A.right, B.right)
def swap(self, root):
return TreeNode(root.val, self.swap(root.right), self.swap(root.left)) if root else None
剑指 Offer 12. 矩阵中的路径
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
if not board or not board[0]: return False
self.board = board
self.word = word
self.m,self.n = len(board), len(board[0])
self.visited = [[False] * self.n for _ in range(self.m)]
for i in range(self.m):
for j in range(self.n):
if self.backtrack(i,j,0):
return True
return False
def backtrack(self, i, j, index):
# 单词已匹配
if len(self.word) == index:
return True
# 越界、字符不匹配、被访问等情况
if i < 0 or i >= self.m or j < 0 or j >= self.n \
or self.board[i][j] != self.word[index] \
or self.visited[i][j]:
return False
self.visited[i][j] = True
# 递归搜索相邻的字符:上下左右搜索
if self.backtrack(i + 1, j, index + 1) \
or self.backtrack(i - 1, j, index + 1) \
or self.backtrack(i, j + 1, index + 1) \
or self.backtrack(i, j - 1, index + 1):
return True
# 回溯,取消当前位置标记
self.visited[i][j] = False
return False
剑指 Offer 13. 机器人的运动范围
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
if m <=0 or n <= 0 or k < 0: return 0
visited = set()
def getDigitSum(num):
res = 0
while num:
res += num % 10
num //= 10
return res
def dfs(i, j):
# 越界、被访问、坐标之和是否等于K
if i < 0 \
or i >= m \
or j < 0 \
or j >= n \
or (i, j) in visited \
or getDigitSum(i) + getDigitSum(j) > k:
return 0
# 标记当前位置已访问
visited.add((i,j))
# 递归搜索相邻位置
return 1 + dfs(i + 1, j) + dfs(i - 1, j) + dfs(i, j + 1) + dfs(i, j - 1)
return dfs(0, 0)
剑指 Offer 34. 二叉树中和为某一值的路径
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def pathSum(self, root: TreeNode, target: int) -> List[List[int]]:
result = []
if not root:
return result
def dfs(node, path, path_cnt):
# 结束条件,走到 node 叶子节点
if not node:
return
path.append(node.val)
path_cnt += node.val
# 递归结果
if not node.left and not node.right and path_cnt == target:
# path[:] 复制 path 内容,放入到 result 中
result.append(path[:])
dfs(node.left, path, path_cnt)
dfs(node.right, path, path_cnt)
path.pop()
dfs(root,[],0)
return result
剑指 Offer 36. 二叉搜索树与双向链表
"""
# Definition for a Node.
class Node:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
"""
class Solution:
def treeToDoublyList(self, root: 'Node') -> 'Node':
if not root: return None
result = []
def dfs(root):
if root:
dfs(root.left)
result.append(root)
dfs(root.right)
# 中序遍历得到有序列表
dfs(root)
# 整理中序遍历列表为双向链表
n = len(result)
for i in range(1, n-1):
result[i].left = result[i-1]
result[i].right = result[i+1]
# 单独处理 head 和 tail 节点
head = result[0]
head.left = result[-1]
head.right = result[1] if n > 1 else head
if n > 1:
tail = head.left
# tail.left 倒数第二个node,right 是 head 节点
tail.left = result[-2]
tail.right = head
return head
剑指 Offer 54. 二叉搜索树的第k大节点
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
s = []
def dfs(root):
if not root: return
dfs(root.left)
s.append(root.val)
dfs(root.right)
# dfs 中序遍历,二叉搜索树结果有序,k 大,则是 -k 数组值
dfs(root)
return s[-k]
剑指 Offer 55 - I. 二叉树的深度
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root: return 0
queue,cnt = deque(),0
queue.append(root)
while queue:
for _ in range(len(queue)):
node = queue.popleft()
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
cnt += 1
return cnt
剑指 Offer 55 - II. 平衡二叉树
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
def recur(root):
if not root: return 0
left = recur(root.left)
if left == -1: return -1
right = recur(root.right)
if right == -1: return -1
return max(left, right) + 1 if abs(left-right) <= 1 else -1
return recur(root) != -1
剑指 Offer 64. 求1+2+…+n
class Solution:
def sumNums(self, n: int) -> int:
if n <=0: return 0
# 0 and num, 返回 0,非0返回num
return n and (n + self.sumNums(n-1))
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
return self.find2(root, p, q)
def find2(self, root, p, q):
# 二叉搜索树,特性:左边
if p.val < root.val > q.val:
return self.find2(root.left, p, q)
# 二叉搜索树,特性:右边
if p.val > root.val < q.val:
return self.find2(root.right, p, q)
return root
def find1(self, root, p, q):
if not root or root == p or root == q:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if not left:
return right
if not right:
return left
return root
剑指 Offer 68 - II. 二叉树的最近公共祖先
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
if not root or root == p or root == q: return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if not left:
return right
if not right:
return left
return root
剑指 Offer 37. 序列化二叉树
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Codec:
# 前序遍历(根左右),preOrder 进行序列化为字符串,字符串反序列化为二叉树
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
if not root:
return "#"
return str(root.val) + "," + self.serialize(root.left) + "," + self.serialize(root.right)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
def helper(nodes):
if not nodes: return None
val = nodes.pop(0) # 弹出左边第一个元素
if val == "#":
return None
node = TreeNode(int(val))
node.left = helper(nodes)
node.right = helper(nodes)
return node
nodes = data.split(",")
return helper(nodes)
# Your Codec object will be instantiated and called as such:
# codec = Codec()
# codec.deserialize(codec.serialize(root))
剑指 Offer 38. 字符串的排列
class Solution:
def permutation(self, s: str) -> List[str]:
res = []
chars = list(s)
def backtrack(start):
if start == len(chars) - 1:
res.append(''.join(chars))
return
visited = set()
for i in range(start, len(chars)):
if chars[i] in visited:
continue
visited.add(chars[i])
chars[start],chars[i] = chars[i],chars[start]
backtrack(start + 1)
# 复原字符数组
chars[start],chars[i] = chars[i],chars[start]
backtrack(0)
return res
分治算法
剑指 Offer 07. 重建二叉树
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
# 前序遍历:根左右
# 中序遍历:左根右
def buildTreeHelper(pre_start, pre_end, in_start, in_end):
if pre_start > pre_end:
return None
root_val = preorder[pre_start]
root = TreeNode(root_val)
# 获取 root_val 在 inorder 中的位置
in_index = inorder.index(root_val)
# 左边有多少个元素: 中序遍历 root 左边元素左子树元素个数
left_size = in_index - in_start
root.left = buildTreeHelper(pre_start + 1, pre_start + left_size, in_start, in_index - 1)
root.right = buildTreeHelper(pre_start + left_size + 1, pre_end, in_index + 1, in_end)
return root
return buildTreeHelper(0, len(preorder)-1, 0, len(inorder)-1)
剑指 Offer 16. 数值的整数次方
class Solution:
def myPow(self, x: float, n: int) -> float:
if n == 0:
return 1
elif n < 0:
return 1 / self.myPow(x, -n)
elif n % 2 == 0:
return self.myPow(x * x, n // 2)
else:
return x * self.myPow(x * x, n // 2)
剑指 Offer 33. 二叉搜索树的后序遍历序列
class Solution:
def verifyPostorder(self, postorder: List[int]) -> bool:
# 后序遍历:左右根
# 中序遍历:左根右
# 前序遍历:根左右
if not postorder:
return True
def helper(start, end):
if start >= end:
return True
root_val = postorder[end]
i = start
# 二叉搜索树,左子树都小于 root_val
while i < end and postorder[i] < root_val:
i += 1
# i 是右子树的第一个值
j = i
while j < end and postorder[j] > root_val:
j += 1
# 右子树能走到倒数第二个节点,才满足右子树都大于 root_val
if j != end:
return False
# 左子树范围:start - (i-1)
left_subtree = helper(start, i - 1)
# 右子树范围:i - (end - 1),需排除 root 节点
right_subtree = helper(i, end - 1)
return left_subtree and right_subtree
return helper(0, len(postorder) - 1)
剑指 Offer 17. 打印从1到最大的n位数
class Solution:
def printNumbers(self, n: int) -> List[int]:
res = []
for i in range(1, pow(10, n)):
res.append(i)
return res
剑指 Offer 51. 数组中的逆序对
class Solution:
def reversePairs(self, nums: List[int]) -> int:
def merge_sort(l, r):
# 终止条件
if l >= r: return 0
# 递归划分
m = (l + r) // 2
res = merge_sort(l, m) + merge_sort(m + 1, r)
# 合并阶段
i, j = l, m + 1
tmp[l:r + 1] = nums[l:r + 1]
for k in range(l, r + 1):
if i == m + 1:
nums[k] = tmp[j]
j += 1
elif j == r + 1 or tmp[i] <= tmp[j]:
nums[k] = tmp[i]
i += 1
else:
nums[k] = tmp[j]
j += 1
res += m - i + 1 # 统计逆序对
return res
tmp = [0] * len(nums)
return merge_sort(0, len(nums) - 1)
# 超时
def s1(self, nums):
cnt = 0
for i in range(0, len(nums)-1):
for j in range(i + 1, len(nums)):
if nums[i] > nums[j]:
cnt += 1
return cnt
排序
剑指 Offer 45. 把数组排成最小的数
class Solution:
def minNumber(self, nums: List[int]) -> str:
# 将数字转换为字符串
nums = [str(num) for num in nums]
# 自定义比较函数
def compare(a, b):
if a + b < b + a:
return -1
elif a + b > b + a:
return 1
else:
return 0
# 使用自定义比较函数排序
nums.sort(key=cmp_to_key(compare))
# 将排序后的字符串数组拼接起来
return ''.join(nums)
剑指 Offer 61. 扑克牌中的顺子
class Solution:
def isStraight(self, nums: List[int]) -> bool:
if not nums: return False
joker = 0
nums.sort()
for i in range(4):
if nums[i] == 0: joker += 1 # 统计大小王数量
elif nums[i] == nums[i + 1]: return False # 重复数字,返回 False
return nums[4] - nums[joker] < 5 # 最大牌 - 最小牌 < 5 则可构成顺子
剑指 Offer 40. 最小的k个数
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
if not arr or k <= 0: return []
arr.sort()
return arr[0:k]
剑指 Offer 41. 数据流中的中位数
class MedianFinder:
def __init__(self):
"""
initialize your data structure here.
"""
self.nums = []
def addNum(self, num: int) -> None:
self.nums.append(num)
def findMedian(self) -> float:
self.nums.sort()
if len(self.nums) % 2 == 1:
return self.nums[len(self.nums)//2]
else:
mid = len(self.nums)//2
return (self.nums[mid] + self.nums[mid-1])/2
# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()
动态规划
剑指 Offer 10- I. 斐波那契数列
class Solution:
def fib(self, n: int) -> int:
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a % 1000000007
剑指 Offer 10- II. 青蛙跳台阶问题
class Solution:
def numWays(self, n: int) -> int:
if n == 0: return 1
if n <= 3: return n
dp = [0] * (n + 1)
dp[0] = 0
dp[1] = 1
dp[2] = 2
for i in range(3, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[-1]%1000000007
剑指 Offer 63. 股票的最大利润
class Solution:
def maxProfit(self, prices: List[int]) -> int:
# return self.s2(prices)
# dp 求解
if not prices: return 0
# 结果
res = 0
# 值存储
profit = [[0 for i in range(3)] for i in range(len(prices))]
# 状态定义: 第一个下标,代表第 0 天,第二个下标,代表操作了多少次(包括买入和卖出)
profit[0][0], profit[0][1], profit[0][2] = 0, -prices[0], 0
# 状态方程
for i in range(1, len(prices)):
profit[i][0] = profit[i-1][0]
profit[i][1] = max(profit[i-1][1], profit[i-1][0] - prices[i])
profit[i][2] = profit[i-1][1] + prices[i]
res = max(res, profit[i][0], profit[i][1], profit[i][2])
return res
def s2(self, prices):
# for 循环,不断修正最小值,当前值 - 最小值 > 0 and > maximum,则替换掉 maximum 的值,循环结束,就得到最大利润
if not prices: return 0
minimum = prices[0]
maximum = 0
for i in prices:
if i < minimum:
minimum = i
if i - minimum > 0 and i - minimum > maximum:
maximum = i - minimum
return maximum
# 暴力法:timeout
def s1(self, prices):
if not prices: return 0
maximum = 0
for i in range(len(prices) - 1):
for j in range(i + 1, len(prices)):
profit = prices[j] - prices[i]
if profit > 0 and profit > maximum:
maximum = profit
return maximum
剑指 Offer 42. 连续子数组的最大和
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
# 递归终止条件
if len(nums) == 1:
return nums[0]
# 分解问题,将数组划分为两部分
mid = len(nums) // 2
left_nums = nums[:mid]
right_nums = nums[mid:]
# 递归求解左右两部分的最大子数组和
left_max = self.maxSubArray(left_nums)
right_max = self.maxSubArray(right_nums)
# 计算跨越中点的最大子数组和
cross_max = self.crossMaxSubArray(nums, mid)
# 返回三者中的最大值作为最终结果
return max(left_max, right_max, cross_max)
def crossMaxSubArray(self, nums, mid):
# 从中点向左扩展求取左半部分的最大子数组和
left_sum = float('-inf')
cur_sum = 0
for i in range(mid-1, -1, -1):
cur_sum += nums[i]
left_sum = max(left_sum, cur_sum)
# 从中点向右扩展求取右半部分的最大子数组和
right_sum = float('-inf')
cur_sum = 0
for i in range(mid, len(nums)):
cur_sum += nums[i]
right_sum = max(right_sum, cur_sum)
# 返回左右两部分的最大子数组和之和
return left_sum + right_sum
def _dp2(self, nums):
if not nums: return 0
dp = [0] * len(nums)
dp[0] = nums[0]
for i in range(1, len(nums)):
# i-1 和大于0,说明有增益效果
if dp[i-1] > 0:
dp[i] = dp[i-1] + nums[i]
else:
dp[i] = nums[i]
return max(dp)
def _dp1(self, nums):
if not nums: return 0
# 结果存储
dp = [0] * len(nums)
# DP 状态初始化
dp[0] = nums[0]
for i in range(1, len(nums)):
# DP 方程
dp[i] = max(dp[i-1] + nums[i], nums[i])
return max(dp)
剑指 Offer 47. 礼物的最大价值
class Solution:
def maxValue(self, grid: List[List[int]]) -> int:
if not grid[0]: return 0
m, n = len(grid), len(grid[0])
dp = [[0] * n for _ in range(m)]
# 初始化第一行和第一列的最大值
dp[0][0] = grid[0][0]
for i in range(1, n):
dp[0][i] = dp[0][i - 1] + grid[0][i]
for j in range(1, m):
dp[j][0] = dp[j - 1][0] + grid[j][0]
# DP 方程:递推计算每个格子的最大价值
for i in range(1, m):
for j in range(1, n):
dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j]
return dp[m-1][n-1]
剑指 Offer 46. 把数字翻译成字符串
class Solution:
def translateNum(self, num: int) -> int:
if num <= 9: return 1
# 0 - 9:有一种翻译方法
# 10-25: 有两种翻译方法
num_str = str(num)
n = len(num_str)
# 创建一个长度为 n+1 的数组 dp,用于保存每个位置的翻译方案数
dp = [0] * (n+1)
# 初始化第一个位置的翻译方案数为 1
dp[0] = 1
# 递推计算每个位置的翻译数
for i in range(1, n + 1):
# 两位数字翻译 -> 1 个字母
if i > 1 and '10' <= num_str[i-2:i] <= '25':
dp[i] = dp[i-1] + dp[i-2]
else:
dp[i] = dp[i-1]
# 返回最后一个位置的翻译方案数
return dp[n]
剑指 Offer 48. 最长不含重复字符的子字符串
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
# 哈希表记录字符和字符最近出现的位置
char_dict = {}
left = 0 # 滑动窗口左边界
max_length = 0 # 字符串最大长度
for right in range(len(s)):
if s[right] in char_dict and char_dict[s[right]] >= left:
# 当前字符重复了,更新左边界为重复字符的下一个位置
left = char_dict[s[right]] + 1
# 更新字符串在哈希表中的位置
char_dict[s[right]] = right
# 计算当前滑动窗口的长度
current_length = right - left + 1
# 更新最长子字符串的长度
max_length = max(max_length, current_length)
return max_length
剑指 Offer 19. 正则表达式匹配
class Solution:
def isMatch(self, s: str, p: str) -> bool:
m, n = len(s), len(p)
def matches(i: int, j: int) -> bool:
if i == 0:
return False
if p[j - 1] == '.':
return True
return s[i - 1] == p[j - 1]
f = [[False] * (n + 1) for _ in range(m + 1)]
f[0][0] = True
for i in range(m + 1):
for j in range(1, n + 1):
if p[j - 1] == '*':
f[i][j] |= f[i][j - 2]
if matches(i, j - 1):
f[i][j] |= f[i - 1][j]
else:
if matches(i, j):
f[i][j] |= f[i - 1][j - 1]
return f[m][n]
剑指 Offer 49. 丑数
class Solution:
def nthUglyNumber(self, n: int) -> int:
ugly = [1] # 存储丑数的数组
p2,p3,p5 = 0,0,0 # 指向下一个丑数要乘以2、3、5的元素的索引
for i in range(1, n):
next_ugly2 = ugly[p2] * 2
next_ugly3 = ugly[p3] * 3
next_ugly5 = ugly[p5] * 5
next_ugly = min(next_ugly2, next_ugly3, next_ugly5)
ugly.append(next_ugly)
# 移动指针
if next_ugly == next_ugly2:
p2 += 1
if next_ugly == next_ugly3:
p3 += 1
if next_ugly == next_ugly5:
p5 += 1
return ugly[n-1]
剑指 Offer 60. n个骰子的点数
class Solution:
def dicesProbability(self, n: int) -> List[float]:
dp = [1/6] * 6
for i in range(2, n + 1):
tmp = [0] * (5 * i + 1)
for j in range(len(dp)):
for k in range(6):
tmp[j + k] += dp[j]/6
dp = tmp
return dp
位运算
剑指 Offer 15. 二进制中1的个数
class Solution:
def hammingWeight(self, n: int) -> int:
res = 0
while n:
res += n & 1
n >>= 1
return res
def s1(self, n):
rst = 0
mask = 1
for i in range(32):
if n & mask:
rst += 1
mask = mask << 1
print(mask)
return rst
剑指 Offer 65. 不用加减乘除做加法
class Solution:
def add(self, a: int, b: int) -> int:
if b == 0: return a
return add(a ^ b, ((a & b) << 1))
剑指 Offer 56 - I. 数组中数字出现的次数
class Solution:
def singleNumbers(self, nums: List[int]) -> List[int]:
nums_dict = {}
for i in nums:
if i in nums_dict:
nums_dict[i] += 1
else:
nums_dict[i] = 1
res = []
for key,value in nums_dict.items():
if value == 1:
res.append(key)
return res
剑指 Offer 56 - II. 数组中数字出现的次数 II
class Solution:
def singleNumber(self, nums: List[int]) -> int:
count = [0] * 32
res = 0
for num in nums:
mask = 1
for i in range(32):
if num & mask:
count[i] += 1
mask <<= 1
for i in range(32):
if count[i]%3 != 0:
res |= (1 << i)
return res
数学
剑指 Offer 39. 数组中出现次数超过一半的数字
class Solution:
def majorityElement(self, nums: List[int]) -> int:
# 排序
nums.sort()
return nums[len(nums)//2]
# 哈希统计
def s1(sefl, nums):
if not nums: return 0
dic = {}
res = 0
for i in nums:
if i in dic:
dic[i] += 1
else:
dic[i] = 1
for key,value in dic.items():
if value > len(nums)//2:
return key
剑指 Offer 66. 构建乘积数组
class Solution:
def constructArr(self, a: List[int]) -> List[int]:
# 结果集中任何一个元素 = 其左边所有元素的乘积 * 其右边所有元素的乘积。一轮循环构建左边的乘积并保存在结果集中,
# 二轮循环 构建右边乘积的过程,乘以左边的乘积,并将最终结果保存
return self.s1(a)
def s1(seflf, a):
b, tmp = [1] * len(a), 1
for i in range(1, len(a)):
b[i] = b[i - 1] * a[i - 1] # 下三角
for i in range(len(a) - 2, -1, -1):
tmp *= a[i + 1] # 上三角
b[i] *= tmp # 下三角 * 上三角
return b
剑指 Offer 14- I. 剪绳子
class Solution:
def cuttingRope(self, n: int) -> int:
dp = [0] * (n + 1)
for i in range(2, n+1):
for j in range(i):
dp[i] = max(dp[i], j* (i-j), j * dp[i-j])
return dp[n]
剑指 Offer 14- II. 剪绳子 II
class Solution:
def cuttingRope(self, n: int) -> int:
dp = [0] * (n + 1)
# n >= 2
for i in range(2, n + 1):
for j in range(i):
dp[i] = max(dp[i], j * (i-j), j * dp[i-j])
return dp[n]%1000000007
剑指 Offer 57 - II. 和为s的连续正数序列
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
i = 1
j = 1
sum = 0
res = []
while i <= target//2:
if sum < target:
sum += j
j += 1
elif sum > target:
sum -= i
i += 1
else:
arr = list(range(i,j))
res.append(arr)
sum -= i
i += 1
return res
剑指 Offer 62. 圆圈中最后剩下的数字
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
f = 0
for i in range(2, n + 1):
f = (m + f) % i
return f
剑指 Offer 43. 1~n 整数中 1 出现的次数
class Solution:
def countDigitOne(self, n: int) -> int:
digit, res = 1, 0
high, cur, low = n // 10, n % 10, 0
while high != 0 or cur != 0:
if cur == 0: res += high * digit
elif cur == 1: res += high * digit + low + 1
else: res += (high + 1) * digit
low += cur * digit
cur = high % 10
high //= 10
digit *= 10
return res
剑指 Offer 44. 数字序列中某一位的数字
class Solution:
def findNthDigit(self, n: int) -> int:
digit = 1 # 数位
count = 9 # 数位范围内的数字个数
start = 1 # 数位范围的起始数字
# 确定目标数字所在的数位范围
while n > count * digit:
n -= count * digit
digit += 1
count *= 10
start *= 10
# 确定目标数字
num = start + (n - 1) // digit
# 确定目标数字中的具体数字
return int(str(num)[(n - 1) % digit])