牛客刷题day7
文章目录
1.删除链表倒数第n个节点
题目
给定一个链表,删除链表的倒数第 nn 个节点并返回链表的头指针
例如,
给出的链表为: 1→2→3→4→5, n= 2
删除了链表的倒数第 n 个节点之后,链表变为1→2→3→5.
解题思路
和前几天删除链表第K个节点思路差不多
都是使用双指针,这里就不赘述删除第K个节点
核心代码
class Solution:
def removeNthFromEnd(self, head, n):
if not head:
return head
fast = head
slow = head
i = 0
while i < n:
fast = fast.next
i += 1
if not fast:
return head.next
while fast.next:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return head
2.两个字符串最长公共子串
题目
给定两个字符串str1和str2,输出两个字符串的最长公共子串
题目保证str1和str2的最长公共子串存在且唯一。
输入:
“1AB2345CD”,“12345EF”
输出:
2345
(其实题目给的案例就有错误,题目说了公共子串最长且唯一,但是这个例子有两个公共子串)
解题思路
1.动态规划
可以将相同的字符串用数组记录下来
2.滑动窗口,用滑动串口记录子字符串长度,并逐渐增大滑动窗口
核心代码
#1.动态规划
class Solution:
def LCS(self, str1, str2):
m, n = len(str1), len(str2)
maxs, ind = 0, 0
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if str1[i - 1] == str2[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
if maxs < dp[i][j]:
maxs = dp[i][j]
ind = i
if maxs == 0:
return ''
else:
return str1[ind - maxs:ind]
#2.滑动串口
class Solution:
def LCS(self , str1 , str2 ):
# r就是滑动串口,a保存最大的公共字符串
r = ''
a = ''
for i in str1:
r += i
if r in str2:
if len(r) > len(a):
a = r
else:
r = r[-1:]
return a
3.最小编辑距离
题目
给定两个字符串str1和str2,再给定三个整数ic,dc和rc,分别代表插入、删除和替换一个字符的代价,请输出将str1编辑成str2的最小代价
解题思路
这个题目考的其实还是动态规划
dp[i][j]表示word1的前i个字符编辑成word2的前j个字符需要的最小操作数
初始状态:dp[i][0] = idc,i次删除;dp[0][j] = icj,j次插入
这里怎么理解 :
怎么把长度为i的字符串转为空串:不就是删除i次
怎么把长度为0的字符串转为长度为j的串,不就是插入j个自符号
判断条件:
当i字符等于j字符时:dp[i][j] = dp[i-1][j-1],不需要额外操作
当i字符不等于j字符时:dp[i][j] = Math.min(insert, delete, replace)
iinsert = dp[i][j-1] + ic; (j-1)是不是比i少一个字符串,是不是插入一个就ok了
那其代价不就是dp[i[[j-1] 的代价加上插入代价
delete = dp[i-1][j] + dc (i-1)是不是比j少一个字符,插入一个不就ok,其代价不就是 dp[i-1][j] + dc
replace = dp[i-1][j-1] + rc; i-1个编辑成j-1个字母,再将i替换成j
这种题目主要理解分类条件前一步和后面一步的联系就好做了
class Solution:
def minEditCost(self, str1, str2, ic, dc, rc):
m, n = len(str1), len(str2)
dp = [[0] * (n + 1) for _ in range(m+1)]
for i in range(m + 1):
dp[i][0] = dc * i
for j in range(n + 1):
dp[0][j] = ic * j
for i in range(1, m + 1):
for j in range(1, n + 1):
if str1[i - 1] == str2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
ic_ = dp[i][j - 1] + ic
dc_ = dp[i - 1][j] + dc
rc_= dp[i - 1][j - 1] + rc
dp[i][j] = min(ic_, dc_, rc_)
return dp[-1][-1]
4. 岛屿数量
题目
给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。
岛屿: 相邻陆地可以组成一个岛屿(相邻:上下左右) 判断岛屿个数。
解题思路
解法1:DFS
从一个为1的根节点开始访问,从每个相邻1节点向下访问到顶点(周围全是水),依次访问其他相邻1节点到顶点
时间复杂度 : O(M×N),其中 M 和 N 分别为行数和列数。
空间复杂度 : 最坏情况下为 O(M×N),此时整个网格均为陆地,深度优先搜索的深度达到 M×N。
解法2:BFS
从一个为1的根节点开始访问,每次向下访问一个节点,直到访问到最后一个顶点
BFS用的是队列—DFS用的是栈,所以DFS直接用递归就可以了,BFS使用列表存储节点就好了
核心代码
# DFS
class Solution:
def solve(self, grid):
# write code here
if not grid:
return 0
m, n = len(grid), len(grid[0])
island_count = 0
for i in range(m):
for j in range(n):
if grid[i][j] == 1:
island_count += 1
self.dfs(grid, i, j)
return island_count
def dfs(self, grid, i, j):
if i < 0 or j < 0 or i >= len(grid) or j >= len(grid[0]) or grid[i][j] == 0:
return
grid[i][j] = 0
# 左上右下
self.dfs(grid, i - 1, j)
self.dfs(grid, i, j - 1)
self.dfs(grid, i + 1, j)
self.dfs(grid, i, j + 1)
#BFS
class Solution:
def solve(self, grid):
# write code here
if not grid:
return 0
m, n = len(grid), len(grid[0])
island_count = 0
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
island_count += 1
self.bfs(grid, i, j, m, n)
return island_count
def bfs(self, grid, i, j, m, n):
queue = []
queue.append(i * n + j)
while queue:
k = queue.pop()
r = int(k / n)
c = int(k % n)
if r - 1 >= 0 and grid[r - 1][c] == '1':
queue.append((r - 1) * n + c)
grid[r - 1][c] = '0'
if r + 1 < m and grid[r + 1][c] == '1':
queue.append((r + 1) * n + c)
grid[r + 1][c] = '0'
if c - 1 >= 0 and grid[r][c - 1] == '1':
queue.append(r * n + c - 1)
grid[r][c - 1] = '0'
if c + 1 < n and grid[r][c + 1] == '1':
queue.append(r * n + c + 1)
grid[r][c + 1] = '0'
5.二叉树深度
题目
求给定二叉树的最大深度,
最大深度是指树的根结点到最远叶子结点的最长路径上结点的数量
解题思路
废话不多说,就是上面的DFS
核心代码
class Solution:
def maxDepth(self, root):
if not root:
return 0
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
6.重建二叉树
题目
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
解题思路
前置知识:
二叉树的前序遍历:根左右
二叉树的中序遍历:左根右
二叉树的的后序遍历:左右根
关键是:利用前序序列根节点在前找到根节点,用根节点去中序序列划分成两部分,左部分是左子树,右部分是右子树。再利用子树长度去前序序列把前序序列中的左右子树找出来,同时可以找出根节点。递归进行此步骤,如果子树长度为0,则不需要生成子问题。
核心代码
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
self.pre = pre
self.tin = tin
self.index_map = {}
m, n = len(pre), len(tin)
for i in range(n):
self.index_map[tin[i]] = i
root = self.build_tree(0, m - 1, 0, n - 1)
return root
def build_tree(self, pre_left, pre_right, tin_left, tin_right):
if pre_left>pre_right:
return
root = self.pre[pre_left]
treeNode = TreeNode(root)
rootTin_index = self.index_map[root]
size = rootTin_index - tin_left
treeNode.left = self.build_tree(pre_left + 1, pre_left + size, tin_left, rootTin_index - 1)
treeNode.right = self.build_tree(pre_left + size + 1, pre_right, rootTin_index + 1, tin_right)
return treeNode
写在后面
博主创作不易,无论是找资料还是写代码都是需要花费时间和精力的,茫茫人海,如果你看到了我的博客,觉得写的还行的话,希望能点赞支持一下,让我更有创作的动力。