Leetcode刷题记录91-100,python语言

91 解码方法(动态规划)

class Solution:
    def numDecodings(self, s: str) -> int:
        if s[0] == "0":  # 如果第一个首字符为0,无法解码,直接返回0. 前导零的情况
            return 0
        dp = [0] * (len(s) + 1)  # 创建长度为s长度加1的动态规划数组,初始化为0
        dp[0] = 1  # 将dp[0]设为1,作为初始值;边界条件
        dp[1] = 1 if s[0] != "0" else 0  # 如果首字符不为0,dp[1]设为1,否则设为0
        for i in range(2, len(s) + 1):  # 从第三个字符开始遍历至字符串结尾
            if 1 <= int(s[i-1:i]) <= 9:  # 如果当前字符可以单独解码,切片操作不包含结束位置i;就是说这里取的s[i-1]
                dp[i] += dp[i-1]  # 则当前解码方法数为前一个字符的解码方法数
            if 10 <= int(s[i-2:i]) <= 26:  # 如果当前字符与前一个字符可以组合解码
                dp[i] += dp[i-2]  # 则当前解码方法数为前两个字符的解码方法数
        return dp[len(s)]  # 返回最终的解码方法数

92 反转链表II(头插法,和206题反转链表相关)

可参考https://blog.csdn.net/kk702392702/article/details/130449146?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-130449146-blog-89846376.235%5Ev43%5Epc_blog_bottom_relevance_base7&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-130449146-blog-89846376.235%5Ev43%5Epc_blog_bottom_relevance_base7&utm_relevant_index=2
如图,可以看图中连线1 2 3,对应for循环里的公式顺序可以看公式1 2 3,对应for循环里的公式顺序

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
        # 创建一个哑节点,指向链表头部
        dummy = ListNode(0)
        dummy.next = head
        # 创建一个哑节点(dummy)并将其指向链表的头部。然后使用指针pre定位到第left个节点的前一个节点。
        # 定位到第left个节点的前一个节点
        pre = dummy
        for _ in range(left - 1): # pre从dummy走left-1次,到达left前一个节点
            pre = pre.next
        
        # 使用头插法反转链表,就是挨个把节点插到cur位置,第一步是3插头变成324,第二步是4插头变成432(把4插到324前面)
        cur = pre.next # curr指针在left节点
        for _ in range(right - left): # 走4-2=2次,中间节点数减一
            temp = cur.next # # 永远指向 currentNode 的下一个节点,循环中需要不断变化
            cur.next = temp.next #可以记忆这四个公式,上一行等号右边和下一行等号左边,是一样的,连接着的指向
            temp.next = pre.next
            pre.next = temp
        
        return dummy.next

93 复原IP地址(递归回溯法)

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        res = []
        n = len(s)
        # 分为四段,每段取值范围为0~255,所以最多只能取3个数字,即左闭右开区间[0, 3]
        def dfs(start, path):
            # 如果分出了4段,并且遍历到了字符串末尾,说明找到了一种合法的IP地址
            if len(path) == 4 and start == n:
                res.append('.'.join(path))
                return
            # 如果已经分出了4段但是还没到字符串末尾,说明这种分法不可行
            if len(path) == 4:
                return
            # 每段取值范围为0~255,所以最多只能取3个数字,三位数,即左闭右开区间[0, 3)
            for i in range(1, 4): # i= 1,2,3,即说有几个数字,在当前小段,0-9一位数,10-99两位数,100-255三位数,
                # 如果当前位置加上i大于字符串长度,说明剩余的字符不足以分成剩下的段数
                if start + i > n: #超出s字符串长度,不可能。start为当前小段的开始位置index
                    break # 直接推出for i循环
                # 取出当前段
                segment = s[start:start+i] # +i是说,当前段的数字几位数,
                # 如果当前段以0开头并且不为0,说明不合法,直接跳过。存在前导0
                if segment[0] == '0' and len(segment) > 1:
                    continue # 跳到下一个for i循环
                # 如果当前段在0~255之间,则继续向下递归搜索
                if int(segment) <= 255:
                    path.append(segment) # 将当前小段加入path list中
                    dfs(start+i, path) # 回溯递归法,树结构下一层,索引开始位置为start+i
                    path.pop() # 删除上一次操作,回溯的意思
        dfs(0, []) # 树结构第一层三个选择,一位数,两位数,三位数
        return res
        
        

94 二叉树的中序遍历 (栈)

在这里插入图片描述
参考https://blog.csdn.net/qq_54185425/article/details/135569960
首先,我们定义了一个二叉树节点类TreeNode,它包含值val、左子节点left和右子节点right。
然后,我们定义了inorderTraversal函数来实现二叉树的中序遍历。该函数接受一个参数root,代表二叉树的根节点。
我们创建了一个空列表result来保存遍历结果。
我们使用一个栈来辅助进行遍历。首先将当前节点指向根节点root。
然后,使用一个循环,不断将当前节点的左子节点压入栈中,直到当前节点为None。
当当前节点为None时,从栈中弹出一个节点,将其值加入到result列表中,并将当前节点指向该节点的右子节点。
重复上述步骤,直到栈为空且当前节点也为None。
最后,返回result作为中序遍历的结果。
我们创建了一个测试样例,并打印出中序遍历的结果。

# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        
        result = [] # 保存结果
        stack = [] # 栈
        curr = root # 当前节点curr起始为根节点
        
        while curr or stack: # 当不为空.记住这里是or不是and
            while curr: # 当curr不为空,
                stack.append(curr) # 栈里依次保存当前节点
                curr = curr.left # 当前节点遍历左子节点
            
            curr = stack.pop() # 当遇到curr为空时即遇到curr.left为空, stack弹出上次让curr.left为空的节点,进行保存。可参考那篇博客的输出结果,遇到左子节点为空,则返回当前节点(作为输出结果,放进result),再遍历右子节点
            result.append(curr.val)
            curr = curr.right
        
        return result

95 不同的二叉搜索树 II (回溯递归)

从start到end范围内遍历每个数字i,将左子树和右子树组合在一起生成新的树,并添加到结果中。
如果传入的n为0,则直接返回空列表。

# 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 generateTrees(self, n: int) -> List[Optional[TreeNode]]:
        ###递归
        def dfs(l,r):  # 左右
            res=[]
            if l>r: return [None] # 左超过右.别忘了这行代码
            for i in range(l,r+1): # 第一次是说从1到n循环i。注意这里是range(左,右+1)
                for x in dfs(l,i-1): # 从i位置划分,左半部分循环x。注意这里是dfs()函数
                    for y in dfs(i+1,r): # 右半部分,循环y
                        root=TreeNode(i) # 依次循环i作为根节点
                        root.left, root.right=x, y #添加左右子节点
                        res.append(root)
            return res # 用了三个for循环
        return dfs(1,n)

96 不同的二叉搜索树 (动态规划,边界条件)

在这里插入图片描述
参考https://blog.csdn.net/cindywry/article/details/106633752
算法参考https://blog.csdn.net/qq_31929377/article/details/106259814
G(n):长度为n的序列的不同二叉搜索树个数。通过画图,左子树的所有取值都是小于i的。注意dp[i]是说整个序列只有i个节点的二叉搜索树个数而不是n个节点以i为根节点的二叉搜索树个数。以j作为根节点,1,2,。。。,j-1作为左子树,j作为根节点,i-j作为右子树。
在这里插入图片描述
numTrees函数用于计算由 1 到 n 构成的不同二叉搜索树的个数。
如果传入的n为0或1,则直接返回1,因为对应着空树或只有一个节点的情况。
创建一个长度为n+1的数组dp用于存储不同节点数量下的二叉搜索树个数,初始化dp[0]和dp[1]为1。
使用动态规划的方法,遍历从2到n的每个节点数量i,在每个节点数量下,再通过二重循环计算以每个节点为根节点时左右子树组合的二叉搜索树个数,累加到dp[i]中。 二重循环参考https://blog.csdn.net/qq_31929377/article/details/106259814
最后返回dp[n],即n个节点构成的不同二叉搜索树的个数。

class Solution:
    def numTrees(self, n: int) -> int:
        if n == 0 or n == 1:
            return 1
        
        dp = [0] * (n + 1) # 保存结果,
        dp[0], dp[1] = 1, 1 # 0和1个节点,返回的为1 和上面if函数一致

        for i in range(2, n + 1): # i循环2到n;表示i依次作为根节点。如n为7,i为3
            for j in range(1, i + 1): # 从1到i循环。表示i之前的节点。j表示左子树的所有取值。如j最多取值1到3个,
                dp[i] += dp[j - 1] * dp[i - j] # 遍历从2到n的每个节点数量i,在每个节点数量下,再通过二重循环计算以每个节点为根节点时左右子树组合的二叉搜索树个数,累加到dp[i]中。 左边有j个。 左子树数字有j-1个,右子树数字有i-j个
                # 注意dp[i]是说整个序列只有i个节点的二叉搜索树个数而不是n个节点以i为根节点的二叉搜索树个数。
                #i-j就是剩下的右子树的取值个数,有多少个就是从1开始到i-j的二叉搜索树的数量,是一致的。dp[i]是说整个序列只有i个节点的二叉搜索树个数而不是n个节点以i为根节点的二叉搜索树个数。所以这里右子树的数字个数就是i-j个数字。
                #左子节点下所有二叉搜索树的数量,乘上,右子节点下所有二叉搜索树的数量。 
                #如以3为根节点。左子树为[1,2],右子树为[4,5,6,7]。因为当前节点要比左子树大,比右子树小。 而且右子树[4,5,6,7]可以组成的不同二叉搜索树的个数与[1,2,3,4]也就是G(4)
        return dp[-1]

97 交错字符串 (动态规划)

我们创建一个二维数组dp,其中dp[i][j]表示s1[0:i]和s2[0:j]是否可以交错形成s3[0:i+j]。即dp[i][j] 表示s1前i个字符和s2前j个字符能否构成s3的前i+j个字符。。然后根据三种情况来更新dp数组,最终返回dp[len(s1)][len(s2)]作为答案。
参考https://blog.csdn.net/qq_38737428/article/details/134583355

class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
        if len(s1) + len(s2) != len(s3):
            return False
        
        dp = [[False for _ in range(len(s2)+1)] for _ in range(len(s1)+1)] # 记住有加一,有不取s1或s2的情况。dp[i][j]为真或假,.,.dp[i][j] 表示s1前i个字符(不包含i)
        # 表示s1前i个字符和s2前j个字符能否构成s3的前i+j个字符.是则为true
        
        for i in range(len(s1)+1): # s1的字符遍历
            for j in range(len(s2)+1): # s2
                if i == 0 and j == 0:
                    dp[i][j] = True # 边界条件
                elif i == 0: # 边界条件,当i为0,s2的前j个字符;直接比较当前s2字符和当前的s3字符是否相等
                    dp[i][j] = dp[i][j-1] and s2[j-1] == s3[i+j-1] #同时满足dp[i][j-1] and s2[j-1] == s3[i+j-1]就为真。
                elif j == 0: # 边界条件 直接比较当前s1字符和当前的s3字符是否相等
                    dp[i][j] = dp[i-1][j] and s1[i-1] == s3[i+j-1]
                else: # dp[i][j] 作为当前s3的前i+j个字符由s1前i个字符和s2前j个字符;
                    #看当前s3的前i+j个字符中最后一个字符串是由谁组成。前一个是说由s2,后一个是说由s1
                    dp[i][j] = (dp[i][j-1] and s2[j-1] == s3[i+j-1]) or (dp[i-1][j] and s1[i-1] == s3[i+j-1])
        # dp[i][j-1] and s2[j-1] == s3[i+j-1]这里是满足括号内两个条件就为真(dp[i][j-1] and s2[j-1] == s3[i+j-1]) ,就是s2的前j-1个字符和s1的前i字符能够组成s3的前i+j-1个,
        return dp[-1][-1]

98 验证二叉搜索树 (递归)

这段代码定义了一个TreeNode类,表示树的节点。然后使用递归函数helper来检查当前节点以及其左右子树是否符合二叉搜索树的性质。对于每个节点,我们比较其值是否在上下界内,并递归地检查其左右子节点。最终返回递归函数helper的结果,判断整棵树是否是二叉搜索树。

# 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:
        def helper(node, lower=float('-inf'), upper=float('inf')): # 定义上下界,传入节点node,之后递归改变上下界取值
            if not node: # 当前节点为空
                return True # 记住这里是True

            #对于每个节点,我们比较其值是否在上下界内,并递归地检查其左右子节点。
            val = node.val # 节点数字
            if val <= lower or val >= upper: 
                return False
            
            if not helper(node.right, val, upper): # 当helper(node.right, val, upper)为假时 执行下面代码
                return False
            if not helper(node.left, lower, val): # 当左节点,超过val上界,说明无效二叉搜索树,返回false
                return False
            
            return True
        
        return helper(root)

99 恢复二叉搜索树 (栈??)

这段代码首先定义了一个TreeNode类来表示二叉树节点。然后使用中序遍历函数inorder_traversal来遍历二叉搜索树,同时寻找需要交换的两个节点。通过记录中序遍历过程中的前一个节点prev,以及当前需要交换的两个节点first和second,最终完成了恢复二叉搜索树的操作。
在这里插入图片描述
1参考https://blog.csdn.net/ARPOSPF/article/details/122676643
2参考https://blog.csdn.net/weixin_51332434/article/details/120605043

# 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 recoverTree(self, root: Optional[TreeNode]) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        first = None
        second = None
        prev = None

        #中序遍历二叉树,并比较上一个节点pre和当前节点的值,如果pre的值大于当前节点值,则记录下这两个节点
        def inorder_traversal(node):
            nonlocal first, second, prev # nonlocal:用来声明外层的局部变量
            if not node: # 节点为空
                return
            
            inorder_traversal(node.left) # 递归,左子节点
            if not prev:
                prev = node # 第一次prev为空,这里就赋值为node了

            if prev and prev.val > node.val: # 当prev为真,
                if not first: 
                    first = prev
                second = node
                
            prev = node
            inorder_traversal(node.right)
        
        inorder_traversal(root)
        
        first.val, second.val = second.val, first.val # 交换这两个节点

法二
在这里插入图片描述

class Solution:
    def recoverTree(self, root: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        firstNode = None
        secondNode = None
        pre = TreeNode(float("-inf"))

        stack = [] 
        p = root
        while p or stack:
            while p:
                stack.append(p)
                p = p.left
            p = stack.pop()
            
            if not firstNode and pre.val > p.val:
                    firstNode = pre
            if firstNode and pre.val > p.val:
                #print(firstNode.val,pre.val, p.val)
                secondNode = p
            pre = p
            p = p.right
        firstNode.val, secondNode.val = secondNode.val, firstNode.val

100 相同的树 (递归!)

上述代码首先定义了树节点类TreeNode,然后实现了isSameTree函数来判断两棵树是否相同。在函数中,使用递归方式比较根节点的值以及左右子树。最终返回True或False表示两棵树是否相同。在示例中创建了两棵相同的树,并调用isSameTree函数进行检查,输出结果为True。

# 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 isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
        # 如果两个树都为空,则相同
        if not p and not q: # 两个树节点都为空
            return True # 注意这里是true,两个为空,就是相同的
        # 如果其中一个树为空,另一个不为空,则不相同
        if not p or not q: 
            return False
        # 如果根节点值不同,则不相同
        if p.val != q.val: # 注意这里是不等于,。而且即使==等于,也不能return true,因为还要看左右子树,这里是比较当前节点
            return False
        # 递归比较左右子树;同时带入左节点,同时带入右节点,进行一一对比
        return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值