参考链接:
剑指Offer(上)01-35题实现 python版本
剑指Offer(下)36-75题实现 Python版本
第 01 天 栈与队列(简单)
剑指 Offer 09. 用两个栈实现队列
栈、设计、队列
class CQueue:
def __init__(self):
self.stack1 = []
self.stack2 = []
def appendTail(self, value: int) -> None:
self.stack1.append(value)
def deleteHead(self) -> int:
if self.stack2:
return self.stack2.pop()
else:
if self.stack1:
while self.stack1:
self.stack2.append(self.stack1.pop())
return self.stack2.pop()
return -1
剑指 Offer 30. 包含min函数的栈
栈、设计
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stackA = []
self.stackB = []
def push(self, x: int) -> None:
self.stackA.append(x)
if not self.stackB or self.stackA[-1] <= self.stackB[-1]:
self.stackB.append(x)
def pop(self) -> None:
if self.stackA.pop() == self.stackB[-1]:
self.stackB.pop()
def top(self) -> int:
return self.stackA[-1]
def min(self) -> int:
return self.stackB[-1]
第 02 天 链表(简单)
剑指 Offer 06. 从尾到头打印链表
栈、递归、链表、双指针
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reversePrint(self, head: ListNode) -> List[int]:
res = []
while head:
res.append(head.val)
head = head.next
return res[::-1]
剑指 Offer 24. 反转链表
递归、链表
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 用两个指针辅助,原链表逆转
# 时间复杂度:O(n), 空间复杂度:O(1)
def reverseList(self, head: ListNode) -> ListNode:
if not head: # 边缘情况
return None
pre = None
cur = head
while cur is not None:
next = cur.next
cur.next = pre
pre = cur
cur = next
return pre
剑指 Offer 35. 复杂链表的复制
哈希表、链表
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
# # 深拷贝,图的深度优先搜索
# def copyRandomList(self, head: 'Node') -> 'Node':
# visited = {} # 哈希表,字典
# return self.dfs(head, visited)
# def dfs(self, head, visited):
# if not head:
# return None
# if head in visited:
# return visited[head]
# copy = Node(head.val, None, None)
# visited[head] = copy
# copy.next = self.dfs(head.next, visited) # 先复制next结点
# copy.random = self.dfs(head.random, visited) # 再复制random结点
# return copy
# # 深拷贝,图的广度优先搜索
# def copyRandomList(self, head: 'Node') -> 'Node':
# visited = {} # 哈希表,字典
# return self.bfs(head, visited)
# def bfs(self, head, visited):
# if not head:
# return None
# copy = Node(head.val, None, None) # 创建新的结点
# queue = [] # 利用队列记录
# queue.append(head)
# visited[head] = copy
# while queue:
# node = queue.pop(0)
# if node.next and node.next not in visited:
# visited[node.next] = Node(node.next.val, None, None)
# queue.append(node.next)
# if node.random and node.random not in visited:
# visited[node.random] = Node(node.random.val, None, None)
# queue.append(node.random)
# visited[node].next = visited.get(node.next, None)
# visited[node].random = visited.get(node.random, None)
# return copy
# 迭代这种方法比较直接,就是分别拷贝next结点和random结点。
def copyRandomList(self, head: 'Node') -> 'Node':
visited = {
} #构建一个链表来记录
def getCloneNode(node):
if node:
if node not in visited:
visited[node] = Node(node.val, None, None)
return visited[node]
else:
return visited[node]
if not head:
return head
old_node = head
new_node = Node(old_node.val, None, None)
visited[old_node] = new_node
while old_node:
new_node.next = getCloneNode(old_node.next)
new_node.random = getCloneNode(old_node.random)
old_node = old_node.next
new_node = new_node.next
return visited[head]
第 03 天 字符串(简单)
剑指 Offer 05. 替换空格
字符串
class Solution:
def replaceSpace(self, s: str) -> str:
return s.replace(' ', "%20")
剑指 Offer 58 - II. 左旋转字符串
数学、双指针、字符串
class Solution:
# 字符串切片
def reverseLeftWords(self, s: str, n: int) -> str:
s1 = s[:n]
s2 = s[n:]
return s2 + s1
# # 旋转操作
# def reverseLeftWords(self, s: str, n: int) -> str:
# s = list(s) # 因为字符串是不可变序列,还有元组
# length = len(s) - 1
# self.reverse(s, 0, n - 1)
# self.reverse(s, n, length)
# self.reverse(s, 0, length)
# return "".join(s)
# def reverse(self, s, start, end):
# while start < end:
# s[start], s[end] = s[end], s[start]
# start += 1
# end -= 1
第 04 天 查找算法(简单)
剑指 Offer 03. 数组中重复的数字
数组、哈希表、排序
class Solution:
def findRepeatNumber(self, nums: List[int]) -> int:
res = collections.Counter(nums)
for key in res:
if res[key] > 1:
return key
剑指 Offer 53 - I. 在排序数组中查找数字 I
数组、二分查找
class Solution:
# # 二分查找,找是target的值的位置
# def search(self, nums: List[int], target: int) -> int:
# left, right = 0, len(nums) - 1
# res = 0
# while left <= right:
# m = (left + right) // 2
# if nums[m] == target: # 若找到目标值后,从中心扩散,寻找上下界
# res += 1
# i = m - 1
# j = m + 1
# while j <= len(nums) - 1 and nums[j] == target:
# res += 1
# j += 1
# while i >= 0 and nums[i] == target:
# res += 1
# i -= 1
# break
# elif nums[m] < target:
# left = m + 1
# else:
# right = m - 1
# return res
# 二分查找,相当于找左右边界,找不是target的值的位置
def search(self, nums: List[int], target: int) -> int:
i, j = 0, len(nums) - 1
while i <= j:
m = (i + j) // 2
if nums[m] <= target: # i想找的是第一个大于target的数
i = m + 1
else:
j = m - 1
right = i # 找右边界
i, j = 0, len(nums) - 1
while i <= j:
m = (i + j) // 2
if nums[m] < target:
i = m + 1 # 尝试查找比target小的更大的数
else: # j想找的是第一个小于target的数
j = m - 1 # 尝试查找大于等于target的更小的数
left = j # 找左边界
return right - left - 1
剑指 Offer 53 - II. 0~n-1中缺失的数字
位运算、数组、哈希表、数学
class Solution:
# 二分查找
def missingNumber(self, nums: List[int]) -> int:
i, j = 0, len(nums) - 1
while i <= j:
mid = (i + j) // 2
if nums[mid] == mid:
i = mid + 1
else:
j = mid - 1
return i
第 05 天 查找算法(中等)
剑指 Offer 04. 二维数组中的查找
数组、二分查找、分治、矩阵
class Solution:
# 二分查找,使得每一次都可以分出两个相反的选择。
def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
if not matrix: # 考虑边界情况,特殊情况
return False
n, m = len(matrix), len(matrix[0]) # 从右上角开始,大的往下找,小的往左找
i, j = 0, m - 1
while 0 <= i < n and 0 <= j < m:
if matrix[i][j] == target:
return True
elif matrix[i][j] < target:
i += 1
else:
j -= 1
return False
剑指 Offer 11. 旋转数组的最小数字
数组、二分查找
class Solution:
# 部分有序,二分查找。找出右排序数组中的第一个数字,也就是旋转点
# 时间复杂度:O(log2N), 空间复杂度:O(1)
def minArray(self, numbers: List[int]) -> int:
i, j = 0 , len(numbers) - 1
while i < j: # 已知j一定在右排序数组中
m = (i + j) // 2 # 当前的中心点
if numbers[m] > numbers[j]: i = m + 1 # 若右侧的点小于中心位置的点,m一定在左排序区间内,则说明旋转点一定在中心位置的右侧
elif numbers[m] < numbers[j]: j = m # 若右侧的点大于中心位置的点,m一定在右排序区间内,则说明旋转点一定在中心位置的左侧
else: j = j - 1 # 若相等,则需要进一步判断, 可参考某大佬评论
'''
若旋转点 x < j, 则上述肯定往左区间找
若旋转点 x = j, 则容易错过旋转点,但可以证明,nums[i]=nums[x]=nums[中心点]=...=nums[j],则最终当i=j时返回的num[i]值等于旋转点的值
还有不用nums[m] 和 nums[i]做比较,因为无法判断 m 在哪个排序数组中
本质上,是因为 j 初始值肯定在右排序数组中; i 初始值无法确定在哪个排序数组中。
对于以下两示例,当 i = 0, j = 4, m = 2 时,有 nums[m] > nums[i] ,而结果不同。
[1, 2, 3, 4, 5] 旋转点 x = 0: m 在右排序数组(此示例只有右排序数组);
[3, 4, 5, 1, 2] 旋转点 x = 3: m 在左排序数组。
'''
return numbers[i]
剑指 Offer 50. 第一个只出现一次的字符
队列、哈希表、字符串、计数
class Solution:
# 利用哈希特征来进行高效查找
def firstUniqChar(self, s: str) -> str:
if not s:
return " "
haxi = dict()
for num in s:
if num in haxi: haxi[num] += 1
else: haxi[num] = 1
for num in haxi:
if haxi[num] == 1:
return num
return " "
第 06 天 搜索与回溯算法(简单)
剑指 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]:
# if root is None:
# return []
# queue1, queue2, res = [], [], []
# queue1.append(root)
# while queue1:
# node = queue1.pop(0)
# res.append(node.val)
# if node.left:
# queue2.append(node.left)
# if node.right:
# queue2.append(node.right)
# if len(queue1) == 0:
# queue1 = queue2
# queue2 = []
# return res
# 二叉树的层次遍历,用队列
def levelOrder(self, root: TreeNode) -> List[int]:
if root is None:
return []
queue, res = [], []
queue.append(root)
while queue:
node = queue.pop(0)
res.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return res
剑指 Offer 32 - 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 root is None:
return []
queue1, queue2, res, temp = [], [], [], []
queue1.append(root)
while queue1:
node = queue1.pop(0)
temp.append(node.val)
if node.left:
queue2.append(node.left)
if node.right:
queue2.append(node.right)
if len(queue1) == 0:
res.append(temp)
temp = []
queue1 = queue2
queue2 = []
return res
剑指 Offer 32 - 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 root is None:
return []
queue1, queue2, res, temp, count = [], [], [], [], 0
queue1.append(root)
while queue1:
node = queue1.pop(0)
temp.append(node.val)
if node.left:
queue2.append(node.left)
if node.right:
queue2.append(node.right)
if len(queue1) == 0:
if count % 2 == 0:
res.append(temp)
temp = []
else:
res.append(temp[::-1])
temp = []
count += 1
queue1 = queue2
queue2 = []
return res
第 07 天 搜索与回溯算法(简单)
剑指 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:
if A == None or B == None:
return False
return self.dfs(A, B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B)
def dfs(self, A, B):
if B == None:
return True
if A == None:
return False
return A.val == B.val and self.dfs(A.left, B.left) and self.dfs(A.right, B.right)
剑指 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:
# if root == None:
# return None
# if root.left != None or root.right != None: # 交换左右子树
# root.left, root.right = root.right, root.left
# self.mirrorTree(root.left)
# self.mirrorTree(root.right)
# return root
# 迭代实现, 用栈实现
def mirrorTree(self, root: TreeNode) -> TreeNode:
stack