169 多数元素
给定一个大小为 n 的数组 nums ,返回其中的多数元素。
思考:这道题的价值在于充分利用了max函数,
class Solution:
def majorityElement(self, nums: List[int]) -> int:
counts = collections.Counter(nums)
return max(counts.keys(), key=counts.get)
二叉树
102 二叉树的层序遍历
思考:下面可以作为层序遍历的模板
# 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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
queue=[]
res=[]
if root:
queue.append(root)
while queue:
size=len(queue)
temp=[]
for i in range(size):
r=queue.pop(0)
temp.append(r.val)
if r.left:
queue.append(r.left)
if r.right:
queue.append(r.right)
res.append(temp)
return res
662 二叉树最大宽度
思考:这题不能一直动态调整queue,要有个辅助队列temp保存下一层的节点,避免了在遍历过程中修改当前队列 queue 的问题
# 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 widthOfBinaryTree(self, root: Optional[TreeNode]) -> int:
queue = []
res = 0
if root:
queue.append([root,1])
while queue:
temp=[]
for node,index in queue:
if node.left:
temp.append([node.left,index*2])
if node.right:
temp.append([node.right,index*2+1])
res=max(res,queue[-1][1]-queue[0][1]+1)
queue=temp
return res
110 平衡二叉树
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def isBalanced(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
def height(root):
if not root:
return 0
else:
return max(height(root.left),height(root.right))+1
if not root:
return True
else:
return abs(height(root.left)-height(root.right))<=1 and self.isBalanced(root.left) and self.isBalanced(root.right)
112 路径总和
判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def hasPathSum(self, root, targetSum):
"""
:type root: TreeNode
:type targetSum: int
:rtype: bool
"""
if not root:
return False
elif not root.left and not root.right:
return root.val==targetSum
else:
return self.hasPathSum(root.left,targetSum-root.val) or self.hasPathSum(root.right,targetSum-root.val)
129 求根节点到叶子节点数字之和
回溯和递归是一一对应的,有一个递归,就要有一个回溯
# 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 sumNumbers(self, root: Optional[TreeNode]) -> int:
self.res=0
r=[]
def dfs(root):
r.append(root.val)
if not root.left and not root.right:
num_str = ''.join(map(str, r))
num = int(num_str)
self.res=self.res+num
if root.left:
dfs(root.left)
r.pop()
if root.right:
dfs(root.right)
r.pop()
if not root:
return 0
else:
dfs(root)
return self.res
226 翻转二叉树
思考:这题主要是在返回值上
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def invertTree(self, root):
"""
:type root: TreeNode
:rtype: TreeNode
"""
if not root:
return root
else:
temp=root.left
root.left=root.right
root.right=temp
self.invertTree(root.left)
self.invertTree(root.right)
return root
105 从前序与中序遍历序列构造二叉树
# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
# 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件
if not preorder:
return None
# 第二步: 前序遍历的第一个就是当前的中间节点
root=TreeNode(preorder[0])
# 第三步: 找切割点
index=inorder.index(preorder[0])
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
left_inorder=inorder[:index]
right_inorder=inorder[index+1:]
# 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
left_preorder=preorder[1:len(left_inorder)+1]
right_preorder=preorder[len(left_inorder)+1:]
# 第六步: 递归
root.left=self.buildTree(left_preorder,left_inorder)
root.right=self.buildTree(right_preorder,right_inorder)
# 第七步: 返回答案
return root
98 验证二叉搜素数
思考:这道题用双指针进行比较,在中序遍历的代码基础上进行了比较,这题要主要对返回值进行判断比较
# 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 isValidBST(self, root: Optional[TreeNode]) -> bool:
self.pre=None
def __isValidBST(root: TreeNode) -> bool:
if not root:
return True
left=__isValidBST(root.left)
if self.pre and self.pre.val>=root.val:
return False
else:
self.pre=root
right=__isValidBST(root.right)
return left and right
return __isValidBST(root)
手撕快速排序
思考:这道题要对快速排序做个优化
- 随机选取分割值
- 要优化数值全部相同的问题
import random
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
def partition(nums,low,high):
mid_id=random.randint(low,high)
nums[low],nums[mid_id]=nums[mid_id],nums[low]
mid=nums[low]
left,right=low,high
while left<right:
while left<right and nums[right]>=mid:
right-=1
nums[left]=nums[right]
while left<right and nums[left]<=mid:
left+=1
nums[right]=nums[left]
nums[left]=mid
return left
def quickSort(nums,low,high):
if low >=high:
return
mid=partition(nums,low,high)
templ=mid-1
tempr=mid+1
while templ>=0 and nums[templ]==nums[mid]:
templ-=1
while tempr <len(nums) and nums[tempr] == nums[mid]:
tempr+=1
quickSort(nums,low,templ)
quickSort(nums,tempr,high)
quickSort(nums,0,len(nums)-1)
return nums
15 三数之和
思考:去重的细节值得考究
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
res=[]
l=len(nums)
for i in range(l-2):
if nums[i]>0:
break
if i>0 and nums[i]==nums[i-1]:
continue
left=i+1
right=l-1
while left<right:
temp=nums[i]+nums[left]+nums[right]
if temp==0:
res.append([nums[i],nums[left],nums[right]])
while left<right and nums[left]==nums[left+1]:
left+=1
while left<right and nums[right]==nums[right-1]:
right-=1
left+=1
right-=1
elif temp<0:
left+=1
else:
right-=1
return res
200 岛屿数量
思考:可以将下面代码记下作为处理岛屿问题的模板
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
lenr=len(grid)
lenc=len(grid[0])
res=0
def inArea(grid,i,j):
return 0<=i<lenr and 0<=j<lenc
def dfs(grid,i,j):
if not inArea(grid,i,j):
return 0
elif grid[i][j]!='1':
return 0
else:
grid[i][j]='2'
dfs(grid,i-1,j)
dfs(grid,i+1,j)
dfs(grid,i,j-1)
dfs(grid,i,j+1)
return 1
for i in range(lenr):
for j in range(lenc):
res+=dfs(grid,i,j)
return res
动态规划
1143 最长公共子序列
思考:这道题逻辑很难,解题的巧妙之处在于dp[i][j]处理的是到text1的i-1和text2的j-1的公共子序列,这样可以化简初始化,把所有工作交给递推公式
递推公式利用了三个方向的值做推导
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
l1=len(text1)+1
l2=len(text2)+1
dp=[[0]*l2 for _ in range(l1)]
for i in range(1,l1):
for j in range(1,l2):
if text1[i-1]==text2[j-1]:
dp[i][j]=dp[i-1][j-1]+1
else:
dp[i][j]=max(dp[i-1][j],dp[i][j-1])
return dp[l1-1][l2-1]
718. 最长重复子数组
class Solution:
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
m=len(nums1)+1
n=len(nums2)+1
result=0
dp=[[0]*n for _ in range(m)]
for i in range(1,m):
for j in range(1,n):
if nums1[i-1]==nums2[j-1]:
dp[i][j]=dp[i-1][j-1]+1
result=max(result,dp[i][j])
return result
221 最大正方形
思考:就像 木桶的短板理论 那样——附近的最小边长,才与 ? 的最长边长有关。
并且本题和上面题的初始化有异曲同工之妙
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
r=len(matrix)+1
c=len(matrix[0])+1
dp=[[0]*c for _ in range(r)]
res=0
for i in range(1,r):
for j in range(1,c):
if matrix[i-1][j-1]=='1':
dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
res=max(res,dp[i][j])
return res*res
链表
92. 反转链表 II
class Solution:
def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
def reverse_linked_list(head: ListNode):
# 也可以使用递归反转一个链表
pre = None
cur = head
while cur:
next = cur.next
cur.next = pre
pre = cur
cur = next
# 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
dummy_node = ListNode(-1)
dummy_node.next = head
pre = dummy_node
# 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
# 建议写在 for 循环里,语义清晰
for _ in range(left - 1):
pre = pre.next
# 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
right_node = pre
for _ in range(right - left + 1):
right_node = right_node.next
# 第 3 步:切断出一个子链表(截取链表)
left_node = pre.next
curr = right_node.next
# 注意:切断链接
pre.next = None
right_node.next = None
# 第 4 步:同第 206 题,反转链表的子区间
reverse_linked_list(left_node)
# 第 5 步:接回到原来的链表中
pre.next = right_node
left_node.next = curr
return dummy_node.next
回溯
回溯算法的模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
93 复原ip地址
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
res = []
current=""
pointsum = 0
length = len(s)
def isvalid(s, startindex, n):
if n - startindex + 1 > 1 and s[startindex] == '0':
return False
substring=s[startindex:n+1]
if substring.isdigit():
num= int(substring)
return 0<=num<=255
else:
return None
def backtraking(startindex, pointsum,current):
if pointsum == 3:
if isvalid(s, startindex, length - 1):
res.append(current+s[startindex:])
return
for i in range(startindex, min(startindex+3,length)):
if isvalid(s, startindex, i):
sub=s[startindex:i + 1] + '.'
backtraking(i + 1, pointsum+1,current+sub)
# pointsum+1,current+sub可不是简洁写法,这就是回溯,相当于pointsum-1,current-sub
backtraking(0, pointsum,current)
return res
39 组合总和
这道题注意剪枝操作,还有一个总是犯错的点:
res.append(path) 将 path 列表的引用添加到 res 中,而不是添加 path 的副本。要解决这个问题,需要在将path 添加到 res 列表时,使用
path.copy()
或者path[:]
来创建 path的副本。这样,每个路径都将独立存储,不会受到后续修改 path 的影响。
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
path=[]
res=[]
def backtracking(startindex,sum):
if sum==target:
res.append(path[:]) # 添加 path 的副本到结果列表
for i in range(startindex,len(candidates)):
if sum+candidates[i]>target:
break #剪枝
path.append(candidates[i])
backtracking(i,sum+candidates[i])
path.pop()
backtracking(0,0)
return res
栈
394 字符串解码
用一个栈保存之前的res,和现在的nums
class Solution:
def decodeString(self, s: str) -> str:
stack=[]
num=0
res=''
for i in range(len(s)):
if s[i]>='0' and s[i]<='9':
num=num*10+int(s[i])
elif s[i]=="[":
stack.append([res,num])
res=''
num=0
elif s[i].isalpha():
res=res+s[i]
else:
last_res,last_num=stack.pop()
res=last_res+res*last_num
return res
二分搜索
34. 在排序数组中查找元素的第一个和最后一个位置
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
n=len(nums)
i,j=0,n-1
while i<=j:
mid=i+(j-i)//2
if nums[mid]>target:
j=mid-1
elif nums[mid]<target:
i=mid+1
else:
i=j=mid
while i-1>=0 and nums[i-1]==target:
i=i-1
while j+1<n and nums[j+1]==target:
j=j+1
return [i,j]
return [-1,-1]
240. 搜索二维矩阵 II
思考:bisect.bisect和bisect.bisect_right返回大于x的第一个下标(相当于C++中的upper_bound),bisect.bisect_left返回大于等于x的第一个下标(相当于C++中的lower_bound)。
import bisect
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
for row in matrix:
tar_id=bisect.bisect_left(row,target)
if tar_id<len(row) and row[tar_id]==target:
return True
return False