系列文章目录
Leetcode刷题:热题HOT100-EASY篇-Python多算法实现(完结-共21题)
Leetcode刷题:热题HOT100-Medium篇-Python多算法实现(完结-1~10题)
Leetcode刷题:热题HOT100-Medium篇-Python多算法实现(完结-11~20题)
文章目录
前言
记录LeetCode 热题 HOT 100的Medium题目算法以及代码实现,采用python实现。
1.最小路径和(动态规划)
1.1 题目描述
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
1.2 核心思路
采用动态规划的思想,设置dp[i][j]表示机器人从起点走到(i,j)时的最小路径和。由于机器人总是向下或者向右行走,因此,dp[i][j]的最小路径和等于机器人从起点到达(i-1,j)的最小路径和【向下走一步到达(i,j)】与从起点到达(i,j-1)的最小路径和【向右走一步到达(i,j)】中的最小值+grid[i][j]
dp[i][j]=min(dp[i-1][j]+dp[i][j-1])+grid[i][j]
同时,需要对dp数组进行初始化。
当i==0时,dp[0][j]=dp[0][j-1]+grid[0][j]
当j==0时,dp[i][0]=dp[i-1][0]+grid[i][0]
1.3 代码
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m,n=len(grid),len(grid[0])
dp=[[0 for i in range(n)] for j 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 i in range(1,m):
dp[i][0]=dp[i-1][0]+grid[i][0]
#根据递推公式更新
for i in range(1,m):
for j in range(1,n):
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j]
return dp[m-1][n-1]
2.单词搜索(回溯)
2.1 题目描述
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
2.2 核心思路
构造递归函数 dfs(board, word, x, y, word, isVisit) 代表以网格的位置 (x, y) 出发,能否搜索到单词 word:
- 如果 board[x][y] != word[0] 返回 False;否则如果当前word为空,则返回 True;
- 标记当前位置被使用 isVisit[x][y] = True,遍历所有相邻位置,如果找到某一个位置有路径可以组成 word,则返回 True;
- 否则把当前位置重新标记为未使用 isVisit[x][y] = False ,返回 False;
2.3 代码
class Solution:
def __init__(self):
self.flag=False
def exist(self, board: List[List[str]], word: str) -> bool:
m,n=len(board),len(board[0])
isVisit=[[False for i in range(n)] for i in range(m)]
def dfs(board,index_x,index_y,word,flag,isVisit):
nonlocal m,n
if len(word)==0 or self.flag:
self.flag=True
return
if index_x>=m or index_x<0 or index_y>=n or index_y<0:
return
if not isVisit[index_x][index_y] and board[index_x][index_y]==word[0]:
isVisit[index_x][index_y]=True
dfs(board,index_x+1,index_y,word[1:],self.flag,isVisit)
dfs(board,index_x-1,index_y,word[1:],self.flag,isVisit)
dfs(board,index_x,index_y+1,word[1:],self.flag,isVisit)
dfs(board,index_x,index_y-1,word[1:],self.flag,isVisit)
isVisit[index_x][index_y]=False
for i in range(m):
for j in range(n):
if board[i][j]==word[0]:
dfs(board,i,j,word,self.flag,isVisit)
if self.flag:
return True
return False
3.验证二叉搜索树(二叉树)
3.1 题目描述
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
输入:root = [2,1,3]
输出:true
3.2 核心思路
二叉搜索树的特性为:中序遍历二叉搜索树,得到一个升序序列。
因此采用中序遍历的方式对二叉搜索树进行遍历,然后判断得到的遍历结果是否为升序即可。
3.3 代码
# 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 __init__(self):
self.result=[]
def isValidBST(self, root: Optional[TreeNode]) -> bool:
#中序遍历
def getSortList(root):
if not root:return
getSortList(root.left)
self.result.append(root.val)
getSortList(root.right)
getSortList(root)
#判断是否为升序
if len(self.result)==1:return True
for i in range(1,len(self.result)):
if self.result[i]<=self.result[i-1]:
return False
return True
4.颜色分类(双指针)*
4.1 题目描述
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库的sort函数的情况下解决这个问题。
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
4.2 核心思路
参考了官方题解:官方题解
4.3 代码
这里给出单指针代码
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
index=0
for i in range(len(nums)):
if nums[i]==0:
nums[index],nums[i]=nums[i],nums[index]
index+=1
head=index
for i in range(head,len(nums)):
if nums[i]==1:
nums[index],nums[i]=nums[i],nums[index]
index+=1
return nums
5.不同的二叉搜索树(二叉树)*
5.1 题目描述
5.2 核心思路
参考了官方题解:官方题解
【方法一:动态规划】
【方法二:卡塔兰数】
5.3 代码
【方法一】
class Solution:
def numTrees(self, n: int) -> int:
G=[0 for i in range(n+1)]
G[0],G[1]=1,1
for i in range(2,n+1):
for j in range(1,i+1):
G[i]+=G[j-1]*G[i-j]
return G[n]
【方法二】
class Solution:
def numTrees(self, n: int) -> int:
C=[0 for i in range(n)]
C[0]=1
for i in range(1,n):
C[i]=(2*(2*i+1)/(i+2))*C[i-1]
return int(C[n-1])
6.二叉树的层序遍历(二叉树,队列)*
6.1 题目描述
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
6.2 核心思路
采用队列的方法实现。以root为根节点的二叉树,若root不为空,则将root放入queue中;然后循环遍历queue:
- 若queue不为空,则弹出队首元素head=queue.pop(),判断head.left和head.right是否为空,若不为空,则将其加入队列。
- 若queue为空,跳出循环。
6.3 代码
# 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]]:
if not root:return []
queue=deque()
queue.append(root)
result=[]
while len(queue)>0:
n=len(queue)
level=[]
for i in range(n):
head=queue.popleft()
if head.left:
queue.append(head.left)
if head.right:
queue.append(head.right)
level.append(head.val)
result.append(level)
return result
7.从前序与中序遍历序列构造二叉树(二叉树)*
7.1 题目描述
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
7.2 核心思路
二叉树前序遍历顺序:根,左,右
二叉树中序遍历顺序:左,根,右
因此可以通过二叉树前序遍历列表的第一个节点,查找到根节点;然后在中序遍历列表中查找根节点的索引,将中序遍历分割为左子树中序遍历列表和右子树中序遍历列表。如下图所示:
采用分治的方法:
- 分:将以root为根节点的二叉树的中序遍历序列和前序遍历序列分割为:左子树的中序遍历序列和前序遍历序列以及右子树中的序遍历序列和前序遍历序列。
- 治:创建root节点存储二叉树前序遍历的第一个元素的值。对左子树的中序遍历序列和前序遍历序列以及右子树中的序遍历序列和前序遍历序列进行划分,同样以根节点为划分点,构造其左子树序列以及右子树序列。并通过递归对其进行合root节点相同的操作。
- 合:将左子树合右子树的返回值赋予root.left 和 root.right。
7.3 代码
# 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 len(preorder)==0:return None
root=TreeNode(preorder[0])
#在中序遍历中查找根节点下标
index=inorder.index(preorder[0])
#子树范围
leftTreeIn,rightTreeIn=inorder[:index],inorder[index+1:]
leftTreePre,rightTreePre=preorder[1:1+index],preorder[1+index:]
root.left=self.buildTree(leftTreePre,leftTreeIn)
root.right=self.buildTree(rightTreePre,rightTreeIn)
return root
8.二叉树展开为链表(二叉树)*
8.1 题目描述
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
8.2 核心思路
参考了官方思路第三种解法。对于当前节点root:
- 如果其左子节点root.left不为空,则在其左子树中找到最右边的节点(右子树的最右节点),作为前驱节点preRight,将当前节点的右子节点root.right赋给前驱节点的右子节点preRight.right,然后将当前节点的左子节点root.left赋给当前节点的右子节点root.right,并将当前节点的左子节点root.left设为None。
- 对当前节点处理结束后,继续处理链表中的下一个节点root.right,直到所有节点都处理结束。
8.3 代码
# 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 flatten(self, root: Optional[TreeNode]) -> None:
"""
Do not return anything, modify root in-place instead.
"""
while root:
if root.left!=None:
#寻找右子树的最右节点
preRight=root.left
while preRight.right:
preRight=preRight.right
#修改节点
preRight.right=root.right
root.right=root.left
root.left=None
root=root.right
9.最长连续序列(哈希表,并查集)*
9.1 题目描述
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
9.2 核心思路
参考了题解:题解
9.3 代码
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
nums_set=set(nums)
hashmap={}
maxLength=0
for i in range(len(nums)):
if nums[i] not in hashmap:
leftLength=hashmap.get(nums[i]-1,0)
rightLength=hashmap.get(nums[i]+1,0)
tempLength=leftLength+rightLength+1
maxLength=max(maxLength,tempLength)
hashmap[nums[i]]=tempLength
hashmap[nums[i]-leftLength]=tempLength
hashmap[nums[i]+rightLength]=tempLength
return maxLength
10.环形链表(双指针)*
10.1 题目描述
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
10.2 核心思路
参考题解:题解
10.3 代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast, slow = head, head
while True:
if not (fast and fast.next): return
fast, slow = fast.next.next, slow.next
if fast == slow: break
fast = head
while fast != slow:
fast, slow = fast.next, slow.next
return fast