牛客刷题day7(删除链表倒数第n个节点,两个字符串最长公共子串,最小编辑距离,岛屿数量,二叉树深度,重建二叉树)

牛客刷题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
        

写在后面

博主创作不易,无论是找资料还是写代码都是需要花费时间和精力的,茫茫人海,如果你看到了我的博客,觉得写的还行的话,希望能点赞支持一下,让我更有创作的动力。
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值