LeetCode中等题合集 python

目录

  • 3. 无重复字符的最长子串
  • 80. 删除有序数组中的重复项 II
  • 442. 数组中重复的数据
  • 209. 长度最小的子数组
  • 59. 螺旋矩阵 II
  • 24. 两两交换链表中的节点
  • 19. 删除链表的倒数第 N 个结点
  • 142. 环形链表 II
  • 236. 二叉树的最近公共祖先
  • 669. 修剪二叉搜索树
  • 701. 二叉搜索树中的插入操作
  • 450. 删除二叉搜索树中的节点
  • 106. 从中序与后序遍历序列构造二叉树
  • 53. 最大子数组和
  • 300. 最长递增子序列
  • 718. 最长重复子数组
  • 1143. 最长公共子序列【重点核心题目】
  • 1035. 不相交的线
  • 392. 判断子序列
  • 583. 两个字符串的删除操作

3. 无重复字符的最长子串

滑动窗口,类似的题目还有绳子覆盖最多的点数
LeetCode中等题合集 python_第1张图片

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 滑动窗口问题
        l,r = 0,0 # 定义窗口的左右边界
        maxn = 0 # 记录最大值

        while l <= len(s)-1: # 左边界可以一直试到最后一个位置
            while (r+1 <= len(s)-1) and (s[r+1] not in s[l:r+1]): # r的增加不能超过右边界
                r+=1

            # 右边界停留在了合适的位置上,计算一下当前的长度吧
            if r-l+1 > maxn:
                maxn = r-l+1

            l+=1 # 左边界滑动一格
            
        return maxn

80. 删除有序数组中的重复项 II

每个数需要和结果集中倒数第二个判断是否相等,是这题的解题关键,因为题目说允许两个数重复。

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:

        if len(nums)<=2:
            return len(nums)

        """
        #只管遍历数组即可,遇到合适的数就往前放(赋值),不合适的就不管这个数了
        #那么前边就形成了一个好的结果集。
        #往前放的时候不用担心,我们会设置一个变量在结果集尾,严格把控人员的进入
        """
        
        # 设置一个看门的变量,前两个元素一定没问题,所以当前的队尾下标就是1
        index = 1
        
        # 查看数组中的每个元素(从第三个元素开始看就行),看能不能放行
        for i in range(2, len(nums)):
            if nums[i] != nums[index-1]: #当前数和结果集中倒数第二个数比较一下  
                nums[index+1] = nums[i]
                index+=1
            
        return index+1

442. 数组中重复的数据

这个题应该属于偏怪的技巧解题吧~~利用数值本身和索引之间的对应关系

class Solution:
    def findDuplicates(self, nums: List[int]) -> List[int]:

        """
        由于数组中的数是有固定范围的,可以对应映射到下标索引上。
        那我们就可以通过操作下标索引位置的那个数,来折射出我们的需求
        """
        
        # 存放找到的结果
        res = []

        # 遍历数组中的每个数
        for num in nums:
            index = abs(num)-1 #这个数的索引

            #找到那个数nums[index]看一下
            if nums[index] < 0: #说明之前有人访问过它了
                res.append(abs(num)) #它不行,它是重复数
            else: #第一次访问
                nums[index] *= -1 #变成负数

        return res

209. 长度最小的子数组

import math
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:

        # 想象成一个队列比较好理解    
        # 左边不需要一个一个往右扩,因为已经算过这一段的和了。
        # 左边挨个削掉数,只需要减一下就算出来新的和了,不需要再重新累加,不断的削减就行
        
        # 只需要定义几个全局变量就够了
        left,right=0,0
        curSum=0
        minLength = 2*len(nums) # 表示一个很大的数

        while right<len(nums):  # 右边可以一直扩展
            curSum+=nums[right] #往里放就行
            right+=1 #右边界准备要放的下一个元素

            # 只要变了我就判断一下当前的和
            while curSum>=target:
                minLength = right-left if right-left<minLength else minLength

                curSum-=nums[left] # 左边削减
                left+=1
        
        return 0 if minLength==2*len(nums) else minLength        

59. 螺旋矩阵 II

定义变量来控制走的边界,要比自己计算每次要走多少步,省事的多
【学习一下python中从大数到小数递减的for循环遍历,如何用range实现】

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:

        # 定义四个边界,边界控制我们要的走墙边的长度
        # 沿着矩阵每走完一个墙边,边界就会压缩一下
        # 这四个边界的值,刚好也能被用来作为访问数组的下标,也就是我们要走的那行/列

        top,bottom,left,right = 0,n-1,0,n-1  #定义边界

        count = 1 #计数
        matrix = [[0 for x in range(n)] for x in range(n)]

        while count <= n**2:
            #沿着上边界右走
            for i in range(left,right+1):
                matrix[top][i] = count
                count += 1
            top += 1 #上边界压缩

            #沿着右边界下走
            for i in range(top,bottom+1):
                matrix[i][right] = count
                count += 1
            right -= 1 #右边界压缩

            #沿着下边界左走
            for i in range(right,left-1,-1): # range(起点,终点,步长)
                matrix[bottom][i] = count
                count += 1
            bottom -= 1 #下边界压缩

            #沿着左边界上走
            for i in range(bottom,top-1,-1):
                matrix[i][left] = count
                count += 1
            left += 1 #左边界压缩

        return matrix

24. 两两交换链表中的节点

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head==None or head.next==None:
            return head
        else:
            newHead = head.next #这是最终要返回的头
            pre = head
            while pre and pre.next: #保持有两个数
                cur = pre.next
                temp = cur.next

                cur.next = pre #核心语句1

                if temp==None or temp.next==None:
                    pre.next = temp
                    return newHead
                else:
                    pre.next = temp.next #核心语句2
                    
                    #往下走两格,进入到下一个循环
                    pre = temp
            
            return newHead

19. 删除链表的倒数第 N 个结点

LeetCode中等题合集 python_第2张图片

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:

        nullHead = ListNode(0,head)

        # 设立双指针cur、pre,想象两者之间构成一个程度为n的滑块
        # 1、pre先走出n步,cur不动,两者之间的距离是n
        # 2、一起走,当pre到末尾,cur指向的是倒数第n个

        pre = nullHead
        cur = nullHead

        while n: #pre走到了正确的位置上
            pre = pre.next
            n -= 1
        
        while pre.next!=None: #pre到最后一个节点时停止
            pre = pre.next
            cur = cur.next
        
        cur.next = cur.next.next #核心语句

        return nullHead.next #返回第一个实节点

142. 环形链表 II

LeetCode中等题合集 python_第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 = head
        slow = head
        
        while fast and fast.next: # 如果是直链,一定会退出while循环的
        
            fast = fast.next.next # 快指针大胆往前走两步
            slow = slow.next # 慢指针走一步
            
            if fast == slow: # 如果他俩相遇了,说明有环
            	#开始模拟,目的是找到环的入口
                index1 = fast #相遇点
                index2 = head #链表起点
                while index1!=index2: #模拟俩人一起走的过程,最终一定会在环入口处相遇
                    index1 = index1.next
                    index2 = index2.next
                return index1
            
        return None

236. 二叉树的最近公共祖先

  • 设计递归函数时,首先要弄设计清楚函数的返回值是什么,怎么利用好子函数的返回值,将子函数的返回值进行整合分析得到最终的一个返回值
  • 以这个题为例,函数的返回值就设计为:该树是否含有p或q,根据子树的返回值,分析当前树的返回值
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
  
        # 根(对根有个初步的判定)、左、右、根(这里重点结合左右子树信息进行操作)

        if root==None: 
            return None

        if root==p or root==q: # 这个节点就是p或q,它就是最近公共祖先,直接返回结果
            return root

        leftSolution = self.lowestCommonAncestor(root.left, p, q) # 左子树返回p、q的存在情况
        rightSolution = self.lowestCommonAncestor(root.right, p, q) # 右子树返回p、q的存在情况

        if leftSolution and rightSolution: # 分析可知,这个根节点就是最近祖先
            return root
        elif leftSolution==None: # 左边没有p或q,右边是有的,继续往上返回右子树的返回结果,只能往上回溯了,说明最近祖先还得往上找
            return rightSolution
        else: # 右边没有p或q,左边是有的,继续往上返回左子树的返回结果,同理只能往上回溯了,说明最近祖先还得往上找
            return leftSolution

669. 修剪二叉搜索树

# 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 trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]:
        # 先审视一下root值
        if root==None:
            return None
        if root.val<low: # 如果这个值都小于左边界了,那它的左子树肯定更小,剪掉!
            return self.trimBST(root.right, low, high) # 只要右子树修剪好的
        elif root.val>high: # 如果这个值都大于右边界了,那它的右子树肯定更大,剪掉!
            return self.trimBST(root.left, low, high) # 只要左子树修剪好的
        else: # root值在正常范围内
            root.left = self.trimBST(root.left, low, high)
            root.right = self.trimBST(root.right, low, high)
            return root

701. 二叉搜索树中的插入操作

思路1:【迭代法】

  • 有针对性地往左/右搜索这棵树,当走到空时,这个节点的位置就是该插入的位置
  • 需要自己时刻记录好上一个节点A,因为我们需要把这个值插入到A上

思路2:【递归法

  • 递归函数的返回值是子树的根节点
  • 也是有选择性地往左/右,不是无脑遍历搜索,走到空时,弄好这个节点进行返回,赋值操作间接来修改指针是灵魂!
  • 正是因为有了递归的返回值,每层递归有栈存储信息,不需要自己记录上个节点
# 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 insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
 
		#节点该在这里插入哈,但是递归法并不是直接进行节点的指针变换进行硬性插入操作
        #而是执行合理的返回操作,往上返回的时候,上一层的节点(递归栈中保存着)会接住它
        if root==None: 
            newNode = TreeNode(val, None, None)
            return newNode #此时它就是一棵小子树,要被上层节点接着,我们进行返回
            
		#接住就是用返回值做赋值,那正好就改变了左/右子树,间接实现了插入的操作
        if val<root.val:
            ltree_returned = self.insertIntoBST(root.left, val) #返回来的左子树
            root.left = ltree_returned #当前节点左指针接住,也就是修改左子树指针
        else:
            rtree_returned = self.insertIntoBST(root.right, val) #返回来的右子树
            root.right = rtree_returned #当前节点右指针接住,也就是修改右子树指针
        return root #返回调整好的当前这棵树

450. 删除二叉搜索树中的节点

LeetCode中等题合集 python_第4张图片

# 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 deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        
        if root==None:
            return None

        if root.val==key: #执行删除操作,并做出适当的返回操作

            ### 此时点的位置很关键(4种)###
            # 1.左右都空
            if root.left==None and root.right==None: 
                return None #把它变成空,也就是往上返回None值,上边会有人接着呢
            # 2.左边不空,右边空
            elif root.left and root.right==None: 
                return root.left #给上边的人返回左子树
            # 3.右边不空,左边空
            elif root.right and root.left==None: 
                return root.right #给上边的人返回右子树
            # 4.左右均不空
            else:
                cur = root.right
                while cur.left:
                    cur = cur.left
                cur.left = root.left
                # 右子树弄妥了,返回右子树给上边的人吧
                return root.right
            
        if key<root.val: #往左继续遍历
            root.left = self.deleteNode(root.left, key)
        if key>root.val: #往右继续遍历
            root.right =  self.deleteNode(root.right, key)
        return root

106. 从中序与后序遍历序列构造二叉树

  • 每次函数进来,都是弄好的列表切片,也就是当前子树
  • 列表每次递归都是变的,其内容只针对当前子树,所以每次做法都是统一的
  • 最终还是要落回到后序列表中分左子树和右子树,因为如果分错了,下次子树的根就不对了
    LeetCode中等题合集 python_第5张图片
    LeetCode中等题合集 python_第6张图片
# 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, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
     
        # 【key】:以后序的列表为基准点,来写终止边界情况
        if len(postorder)==0:
            return None
        if len(postorder)==1: #只有一个节点时,这棵子树就是它自己,直接完事了
            newNode = TreeNode(postorder[-1], None, None)
            return newNode

        #有多个节点时,就需要先构建出左右子树赋值给它才能往上返回了
        rootVal = postorder[-1]
        # 构建出当前节点
        curRoot = TreeNode(rootVal, None, None)
        rootIndex = inorder.index(rootVal)
        # 期待着左子树
        curRoot.left = self.buildTree(inorder[:rootIndex], postorder[:rootIndex])
        # 期待着右子树
        curRoot.right = self.buildTree(inorder[rootIndex+1:], postorder[rootIndex:len(postorder)-1])
        # 弄好了就返回当前这棵树
        return curRoot

53. 最大子数组和

动态规划,解题思路看我的这篇博客

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:

        # 记录最大和
        max_sum = nums[0]  #边界就是0号元素
        # 直接用一个变量记录一下前边算出来的值,因为每个位置上只和前一个元素有关,省去dp数组
        dp_cur = nums[0] #0号元素没法往前扩,只有一种选择,就是它自己就是累加和

        for i in range(1, len(nums)):
            dp_cur = max(dp_cur+nums[i], nums[i]) #”前边算出来的dp值+自身值“和”自身值“比大小
            max_sum = max(max_sum, dp_cur)

        return max_sum

300. 最长递增子序列

  • 和上个题一样,以 i 结尾,而不是以 i 开头。
  • 当前值作为结尾形成的序列,就能把长度给严格卡住卡死了,那值就是确定的唯一的了。
  • 和 i 之前的所有数 j 进行比较,如果 i 的值比 j 大,那么可以考虑是否追随 j 的脚步,让以 j 结尾的最长序列加1作为 i 结尾的最长序列的一个备选方案。一个都不追随的话,最次的情况就是自己一个元素做序列(结尾),此时长度为1。
  • 最终要求的结果是,以哪个 i 为结尾序列长度最大,也就是dp[i]中的最大值。
    LeetCode中等题合集 python_第7张图片
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        # 1、dp[i]:以i为结尾的最长子序列长度
        # 2、递推公式:dp[i] = dp[j]+1 (if nums[i]>nums[j])
        # 3、初始化:dp[0]=1
        # 4、遍历顺序:从小到大
        # 5、确定返回值
        
        dp = [1] * len(nums)
        maxLen = 1

        for i in range(1, len(nums)): # 以i结尾的情况
            # 下边的for循环,为了找到最优的dp[i]
            for j in range(0, i): # 查看i前边的每个j长度情况
                if nums[i]>nums[j]:
                    dp[i] = max(dp[i], dp[j]+1)
            # 在dp[i]中还要找到最优的那个
            maxLen = max(maxLen, dp[i])

        return maxLen

类似的题目可以查看【1】简单题合集中的674题目

718. 最长重复子数组

  • 两个for循环分别遍历两个数组,构造出一个二维的dp数组
  • dp[i][j] = dp[i-1][j-1]+1
  • 当前dp值依赖于前边,而第一个位置上就已经开始依赖前边了,所以边界值需要扩充出来一行一列进行构造,所以dp的填充是从1开始的,而数组的元素遍历还是从0开始
class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        maxLen = 0

        # 因为当前dp值要基于上一个dp来算,而边界是0处
        # 所以多弄出来一行一列进行了扩充,作为边界即初始值
        row, col = len(nums1)+1, len(nums2)+1 
        dp = [[0]*col for i in range(row)] # 创建二维dp数组,


        # dp数组从下标为1的位置开始填值,我们的视角在dp身上
        for i in range(1, len(nums1)+1):
            for j in range(1, len(nums2)+1):
                if nums1[i-1] == nums2[j-1]: #dp视角对应的num数组下标应该是减1的
                    dp[i][j] = dp[i-1][j-1]+1 #修改当前dp[i][j]的值
                
                maxLen = max(maxLen, dp[i][j])

        return maxLen 

1143. 最长公共子序列【重点核心题目】

和上个题基本一样,只多加了一条语句
LeetCode中等题合集 python_第8张图片

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        maxLen = 0
        row,col = len(text1)+1, len(text2)+1
        dp = [[0]*col for i in range(row)]

        for i in range(1, row):
            for j in range(1, col):
                if text1[i-1]==text2[j-1]:
                    dp[i][j] = dp[i-1][j-1]+1
                else: #即使不相等,dp状态也要被更新
                	# 那A序列就刨掉最后一个数好了,去和当前B序列求最长公共子序列长度
                	# 那B序列也刨掉最后一个数试试,去和当前A序列求最长公共子序列长度
                	# 选择他俩舍弃后更优的那个结果
                    dp[i][j] = max(dp[i][j-1], dp[i-1][j]) 
         
                maxLen = max(maxLen, dp[i][j])
        
        return maxLen       

1035. 不相交的线

读懂题目后,和上个题(最长公共子序列)代码完全一样

class Solution:
    def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:
        maxLen = 0
        row = len(nums1)+1
        col = len(nums2)+1
        dp = [[0]*col for i in range(row)]
        
        for i in range(1, row):
            for j in range(1, col):
                if nums1[i-1]==nums2[j-1]:
                    dp[i][j] = dp[i-1][j-1]+1
                else:
                    dp[i][j] = max(dp[i][j-1], dp[i-1][j])
                
                maxLen = max(maxLen, dp[i][j])
        
        return maxLen

392. 判断子序列

  • 和之前的题不一样的是,此时的最优值一定是在最后产出,也就是dp数组的最后
  • 之前的题目中,最长公共子序列可以是序列的中间产生,所以在整个dp数组中找最值
  • 但是现在我们要的就是s序列的全部长度,最值必须且一定是在最后
    LeetCode中等题合集 python_第9张图片
class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        row = len(s)+1
        col = len(t)+1
        dp = [[0]*col for i in range(row)]
        
        for i in range(1, row):
            for j in range(1, col):
                if s[i-1]==t[j-1]:
                    dp[i][j] = dp[i-1][j-1]+1
                else:
                    dp[i][j] = dp[i][j-1] # s是不会退步的,没有i-1的那个情况

        if dp[-1][-1]==len(s):
            return True
        else:
            return False

583. 两个字符串的删除操作

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        # 求最长公共子序列的长度,两个字符串的长度分别减去公共长度,就是最小步数和

        row = len(word1)+1
        col = len(word2)+1
        dp = [[0]*col for i in range(row)]
        maxLen = 0
        minStep = 0

        for i in range(1, row):
            for j in range(1, col):
                if word1[i-1]==word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]+1
                else:
                    dp[i][j] = max(dp[i][j-1], dp[i-1][j])
                maxLen = max(maxLen, dp[i][j])

        minStep = len(word1)-maxLen+len(word2)-maxLen

        return minStep

持续更新中……

你可能感兴趣的:(#,Leetcode,python,leetcode,刷题,算法,每日一题)