热题100_medium

leetcode热题100在之前的分类总结中已经完成了一部分,现在完成剩下的一部分问题

  • 5 最长回文子串 (medium)
  • 22 括号生成 (medium)
  • 33 搜索旋转排序数组 (medium)
  • 48 旋转图像 (medium)
  • 55 跳跃游戏 (medium)
  • 56 合并区间 (medium)
  • 96 不同的二叉搜索树 (medium)
  • 105 从前序与中序遍历序列构造二叉树 (medium)
  • 114 二叉树展开为链表 (medium)
  • 142 环形链表 II (medium)
  • 146 LRU缓存机制 (medium)
  • 148 排序链表 (medium)
  • 152 乘积最大子数组 (medium)
  • 208 实现 Trie (前缀树) (medium)
  • 221 最大正方形 (medium)
  • 238 除自身以外数组的乘积 (medium)

5 最长回文子串 (medium)

https://leetcode-cn.com/problems/longest-palindromic-substring/

class Solution:
    def longestPalindrome(self, s: str) -> str:
        '''
        暴力法,求得所有的字串,然后判断是否是回文串(超时)
        '''
        if not s: return ''
        max_len = 0
        res = ''
        for i in range(len(s)):
            for j in range(i, len(s)):
                if s[i:j+1] == s[i:j+1][::-1]:
                    if max_len < j-i+1:
                        max_len = j-i+1
                        res = s[i:j+1]
        return res      
class Solution:
    def longestPalindrome(self, s: str) -> str:
        '''
        暴力法存在大量的重叠子问题,例如当判断s[1:5]是否为回文串,判断了s[2:4]是否为回文串
        采用动规的方式解决重叠子问题,以空间换取时间
        状态:dp[i][j]表示从i到j是否为回文串
        状态转移方程:dp[i][j] = dp[i+1][j-1] and s[i] == s[j]
        边界条件:当仅有一个元素时:dp[i][i] = True
                当仅有俩元素时,dp[i][j] = s[i] == s[j]
        '''
        if not s: return ''
        l = len(s)
        dp = [[False]*l for _ in range(l)]
        # 仅有一个元素时的边界条件
        for i in range(l):
            dp[i][i] = True

        max_len = 1
        start = 0
        for j in range(1, l):
            for i in range(0, j):
                if s[i] == s[j]:
                # 仅有两个元素时的条件
                    if j - i < 3:
                        dp[i][j] = True
                    else:
                        dp[i][j] = dp[i+1][j-1]
                else: dp[i][j] = False
                ## 记录最长的长度
                if dp[i][j]:
                    cur_len = j - i + 1
                    if cur_len > max_len:
                        max_len = cur_len
                        start = i
        return s[start:start + max_len]

22 括号生成 (medium)

https://leetcode-cn.com/problems/generate-parentheses/

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        '''
        暴力递归,超时
        '''
        def helper(s, path):
            if not s:
                if isvalid(path) and path not in self.res:
                    self.res.append(path)
                return 

            for i in range(len(s)):
                left = path.count('(')
                right = path.count(')')
                if  right> left: continue
                if left == right and s[i] == ')': continue
                helper(s[:i]+s[i+1:], path + s[i])

        def isvalid(s):
            stack = []
            for char in s:
                if char == '(':
                    stack.append(char)
                else:
                    if stack:
                        stack.pop()
                    else:
                        return False
            if not stack: return True 
        
        if not s: return []
        s = '(' *n + ')'*n
        self.res = []
        helper(s, '')
        return self.res
class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        '''
        上边的递归存在大量的重复解
        '''

        def helper(left, right, path):
            if left == n and right == n:
                self.res.append(path)
                return 
            # 这种情况构不成有效括号
            if left < right:
                return 
            ## 如果左括号已经使用的数目没有达到n,左括号还有一些没有使用
            if left < n:
                helper(left + 1, right, path +'(')
            # 右括号还有一些没有使用
            if right < n:
                helper(left, right + 1, path + ')')

        self.res = []
        helper(0, 0, '')
        return self.res

33 搜索旋转排序数组 (medium)

https://leetcode-cn.com/problems/search-in-rotated-sorted-array/

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        '''
        限制时间复杂度为logn,考虑到二分查找,对于二分查找主要的工作就是对于左右区间的拆分判断
        对于[4,5,6,7,0,1,2], 对于给定的0, 左右两部分为【4,5,6】,【7,0,1,2,3】
        两部分中肯定有一部分为有序的,
        如果nums[l]nums[l],存在于左边部分
                            否则位于右边部分
        如果右边有序,如果target>nums[mid] 位于右边部分
                        否则位于左边
        '''

        l, r = 0, len(nums)-1
        while l <= r:
            mid = l + (r-l)//2
            # print('*',mid,/ l, r)
            # 如果值刚好相等,就返回
            if target == nums[mid]: return mid
            # 如果左边序列有序

            if nums[l] <= nums[mid]:
                # target位于左半块
                if nums[l]<=target<nums[mid]:
                    r = mid-1
                # target位于右半块
                else:
                    l = mid+1
            # 左半块无序
            else:
                # target位于右半块(右半块有序)
                if nums[mid]<target <=nums[r] :
                    l = mid + 1
                else:
                    r = mid - 1
            # print(mid, l, r)
        return -1

48 旋转图像 (medium)

https://leetcode-cn.com/problems/rotate-image/

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        旋转90度就是转置加上翻转
        """
        if not matrix: return 

        n = len(matrix)
        # 转置
        for i in range(n):
            for j in range(i+1, n):
                if i ==j: continue
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] 
        #翻转每一行
        for i in range(n):
            matrix[i] = matrix[i][::-1]

55 跳跃游戏 (medium)

https://leetcode-cn.com/problems/jump-game/

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        '''
        宽度优先遍历,看是否可以达到最后(超时)
        '''
        from collections import deque

        if not nums: return True

        target = len(nums)-1
        queue = deque()
        queue.append((0,nums[0]))
        while queue:
            start_index, step = queue.pop()
            if start_index == target: return True
            # if step == 0 and start_index != target: continue
            for i in range(1, step+1):
                if i + start_index <= target:
                    # print(start_index+i, start_index, step)
                    queue.append((start_index+i, nums[start_index+i]))
                    if start_index+i == target: return True
        return False
class Solution:
    def canJump(self, nums: List[int]) -> bool:
        '''
        利用贪心算法
        构建数组,记录从第i个位置起始可以到达的最远位置
        '''
        if not nums: return True
        # 利用reach数组存储每个位置起始可以达到的最远位置
        reach = [nums[0]]

        for i in range(1, len(nums)):
            # 如果最远可到达位置超过数组,那么说明可以到达最后一个位置
            if reach[i-1] >= len(nums)-1: return True
            # 如果第i个位置可以达到,则利用reach记录最大的位置
            if i <= reach[i-1]:
                reach.append(i + nums[i])
            # 如果达不到则返回False
            else:
                return False
        return True

56 合并区间 (medium)

https://leetcode-cn.com/problems/merge-intervals/

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        '''
        按照第一个元素排序
        第一种情况,i+1的第一个数大于i的第二个数,这两个区间不重合,保存第i个区间
        第二种情况,i+1的第一个数小于i的第二个数,然后这俩区间合并
        '''
        if not intervals: return []
        if len(intervals) == 1: return intervals

        intervals.sort(key=lambda x: x[0])
        # print(intervals)
        left = intervals[0][0]
        right = intervals[0][1]
        res = []
        for i in range(len(intervals)-1):
            if intervals[i+1][0] > right:
                res.append([left, right])
                left = intervals[i+1][0]
                right = intervals[i+1][1]
            else:
                right = max(right, intervals[i+1][1])
        res.append([left, right])
        return res

96 不同的二叉搜索树 (medium)

https://leetcode-cn.com/problems/unique-binary-search-trees/

class Solution:
    def numTrees(self, n: int) -> List[TreeNode]:
        '''
        采用递归的方法,直接求解(超时)
        '''
        if(n==0):
            return 1
        def build_Trees(left,right):
            all_trees=[]
            if (left>right):
                return 1
            num = 0
            for i in range(left,right+1):
                left_trees=build_Trees(left,i-1)
                right_trees=build_Trees(i+1,right)
                num += left_trees * right_trees
            return num
        res=build_Trees(1,n)
        return res
class Solution:
    def numTrees(self, n):
        """
        假设n个节点存在

        令G(n)的从1到n可以形成二叉排序树个数

        令f(i)为以i为根的二叉搜索树的个数

        即有:G(n) = f(1) + f(2) + ... + f(n)

        n为根节点,当i为根节点时,其左子树节点个数为[1,2,3,...,i-1],右子树节点个数为[i+1,i+2,...n],所以当i为根节点时,其左子树节点个数为i-1个,右子树节点为n-i,即f(i) = G(i-1)*G(n-i),

        上面两式可得:G(n) = G(0)*G(n-1)+G(1)*(n-2)+...+G(n-1)*G(0)

        """
        G = [0]*(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]

105 从前序与中序遍历序列构造二叉树 (medium)

https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        '''
        前序遍历:根——左——右
        中序遍历:左——根——右
        1、利用前序遍历找到二叉树的根节点,然后在对应的中序遍历中找到这个节点
        2、在中序遍历中,根节点将数组分为两部分,左右两部分分别为左右子树,得到左右子树的节点个数
        3、得到左右子树的前序遍历与中序遍历结果,递归处理
        '''
        def helper(preorder_left, preorder_right, inorder_left, inorder_right):
            '''
            四个参数分别表示前序遍历的左右节点的索引,中序遍历的左右节点的索引
            '''
            if preorder_left > preorder_right: return
            # 前序遍历的第一个节点就是根节点
            preorder_root_index = preorder_left
            # 中序遍历中的根节点的索引
            inorder_root_index = hashmap[preorder[preorder_root_index]]
            # 构建根节点
            root = TreeNode(preorder[preorder_root_index])
            # 左子树的节点数目
            left_tree_size = inorder_root_index - inorder_left
            # 递归构建左子树
            root.left = helper(preorder_root_index+1, preorder_root_index+left_tree_size, inorder_left, inorder_root_index-1)
            # 递归构建右子树
            root.right = helper(preorder_root_index+left_tree_size+1, preorder_right, inorder_root_index+1, inorder_right)
            return root
        # 构建元素-索引字典(提高搜索根节点的效率)
        hashmap = {j: i for i, j in enumerate(inorder)}

        return helper(0, len(preorder)-1, 0, len(inorder)-1)   

114 二叉树展开为链表 (medium)

https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/

# 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: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        展开的链表为其前序遍历的结果
        1、先找到左子树最右边的节点,将右子树接到左子树最右边节点上
        2、将左子树移动到原来右子树的位置
        3、清空左子树,完成了第一个点
        4、递归搜寻树的右子树
        """
        def helper(root):
            if not root: return
            cur_node = root.left
            if cur_node:
                # 找到左子树的最右边节点
                while cur_node.right:
                    cur_node = cur_node.right
                #右子树接到左子树最右边节点上
                cur_node.right = root.right
                # 左子树接在原来右子树的位置
                root.right = root.left
                # 清空左子树
                root.left = None
            # 递归搜寻右子树
            helper(root.right)
        helper(root)

142 环形链表 II (medium)

https://leetcode-cn.com/problems/linked-list-cycle-ii/

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        '''
        哈希表
        '''
        hashmap = set()
        if not head: return

        while head:
            if head not in hashmap:
                hashmap.add(head)
            else:
                return head
            head = head.next
        return
class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        '''
        环形链表自然而然想到快慢指针
        1、利用快慢指针寻找是否出现环,如果没有环,返回null
        2、如果出现环,利用双指针找寻入口的节点
            第一个指针指向头部,第二个指针指向快慢指针相遇的节点
            二者再次相遇的节点就是入口节点
        具体分析参考:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/linked-list-cycle-ii-kuai-man-zhi-zhen-shuang-zhi-/
        '''
        if not head: return
        # s为慢指针,f为快指针
        s, f =head, head
        while f and f.next:
            s, f = s.next, f.next.next
            if f == s: break
        # 没有环
        if not f or not f.next: return
        # s慢指针即为二者相遇的节点
        while s != head:
            s, head = s.next, head.next
        return s

146 LRU缓存机制 (medium)

https://leetcode-cn.com/problems/lru-cache/

from collections import OrderedDict
class LRUCache:
    '''
    利用python自带的orderdict解决,实际就是双向链表加hashmap的结构
    '''
    def __init__(self, capacity: int):
        self.maxsize = capacity
        self.lrucache = OrderedDict()
    def get(self, key: int) -> int:
        if key in self.lrucache:
            # 将现有 key 移动到有序字典的任一端。 如果 last 为真值(默认)则将元素移至末尾;如果 last 为假值则将元素移至开头
            self.lrucache.move_to_end(key)
        return self.lrucache.get(key,-1)
    def put(self, key: int, value: int) -> None:
        if key in self.lrucache:
            del self.lrucache[key]
        self.lrucache[key] = value 
        if len(self.lrucache)>self.maxsize:
            # 如果 last 值为真,则按 LIFO 后进先出的顺序返回键值对,否则就按 FIFO 先进先出的顺序返回键值对。
            self.lrucache.popitem(last = False) 
'''
利用hashmap加上双向链表实现功能
'''
class Node:
    def __init__(self,key,val):
        self.key=key
        self.val=val
        self.next=None
        self.prev=None

class LRUCache:
    def __init__(self, capacity: int):
        self.dic={}
        self.head=Node(None,None)
        self.tail=Node(None,None)
        self.head.next=self.tail
        self.tail.prev=self.head
        self.capacity=capacity

    def get(self, key: int) -> int:
        if not key in self.dic:
            return -1
        node=self.dic[key]
        self.delete(node)
        self.insert(node)
        return node.val        

    def insert(self,node):
        node.next,node.prev=self.head.next,self.head
        temp=self.head.next
        self.head.next=node
        temp.prev=node

    def delete(self,node):
        node.prev.next,node.next.prev=node.next,node.prev

    def put(self, key: int, value: int) -> None:
        if key in self.dic:
            node=self.dic[key]
            node.val=value
            self.delete(node)
            self.insert(node)
            return        
        if len(self.dic)==self.capacity:
            node=self.tail.prev
            self.delete(node)
            del self.dic[node.key]
        node=Node(key,value)
        self.dic[key]=node
        self.insert(node)

148 排序链表 (medium)

https://leetcode-cn.com/problems/sort-list/

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        '''
        nlogn的时间复杂度,常数级空间,采用归并排序,采用递归(这样空间复杂度不为常数,因为采用了递归栈
        '''
        def helper(head):
            if not head or not head.next: return head
            ## 寻找中间节点mid,对于偶数个节点,slow指向左半段的最后一个元素 对于奇数节点,slower位于最中间的元素
            slow, fast = head, head.next
            while fast and fast.next:
                slow, fast = slow.next, fast.next.next
            # 切分两块链表
            mid, slow.next = slow.next, None
            left = helper(head)
            right = helper(mid)
            # print(left, right)
            # 融合左右两部分链表
            # dummy node
            h = res = ListNode(-1)
            while left and right:
                if left.val < right.val: h.next, left =left, left.next
                else:
                    h.next, right = right, right.next
                h = h.next
            h.next = left if left else right
            return res.next
        return helper(head)
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        '''
        nlogn的时间复杂度,常数级空间,采用自底而上的归并排序
        具体思路就是,先两个两个merge,然后四个四个merge,持续类推,直到排序完成
        例: [3,54,24,9,35,43,27,11]
        1、(3,54)(9,24)(35,43)(11,27)
        2、(3,9,24,54)(11,27,35,43)
        3、(3,9,11,24,27,35,43,54) 排序完成
        '''
        # 计算链表的长度
        L = 0 #链表的长度
        p = head
        while p:
            L += 1
            p = p.next
        
        # 构建虚拟头节点
        dummy = ListNode(-1)
        dummy.next = head
        
        step = 0
        while step <= L:
            cur = dummy.next
            tail = dummy
            while cur:
                left = cur
                right = self.cut(left, step)
                cur = self.cut(right, step)
                tail.next = self.merge(left, right)
                while tail.next:
                    tail = tail.next
            # print(dummy.next)
            step = step * 2 + 1
        return dummy.next
    
    def cut(self, head, step):
        """
        从链表中截取步长为step的段
        step的值为1,3,7 ... ,截取的步长为2,4,8 ... 
        返回截断后半部分的第一个节点
        """
        p = head
        while p and step:
            p = p.next
            step -= 1

        if not p:
            return None

        next_ = p.next  # 返回截断的后半部分
        p.next = None  # 截断
        return next_

    def merge(self, left, right):
        '''
        融合左右两段
        '''
        dummyHead = ListNode(0)  # 虚拟头节点
        p = dummyHead
        while left and right:
            if left.val < right.val:
                p.next = left
                p = left
                left = left.next
            else:
                p.next = right
                p = right
                right = right.next
        p.next = left if left else right
        return dummyHead.next  # 返回排序后的结果

152 乘积最大子数组 (medium)

https://leetcode-cn.com/problems/maximum-product-subarray/

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        '''
        暴力法,遍历所有连续子数组,然后保留最大的乘积(超时)
        '''
        max_value = float('-inf')

        for i in range(len(nums)):
            product = nums[i]
            max_value = max(max_value, product)
            for j in range(i+1, len(nums)):
                product *= nums[j]
                max_value = max(max_value, product)
        return max_value
class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        '''(可以优化为只用两个变量)
        动态规划,dp1[i]表示到第i个元素为止连续子数组的最大乘积,dp2[i]表示到第i个元素为止连续子数组的最小乘积
        状态转移方程:
            dp1[i] = max(dp1[i-1]*nums[i], dp2[i-1]*nums[i], nums[i])
            dp2[i] = min(dp1[i-1]*nums[i], dp2[i-1]*nums[i], nums[i])
        边界条件dp1[0] = nums[0]
        '''
        if len(nums)==1: return nums[0]
        dp1 = [float('-inf')] * len(nums)
        dp2 = [float('inf')] * len(nums)
        res = float('-inf')
        dp1[0] = nums[0]
        dp2[0] = nums[0]
        res = max(res, dp1[0], dp2[0])
        for i in range(1, len(nums)):
            dp1[i] = max(dp1[i-1]*nums[i], dp2[i-1]*nums[i], nums[i])
            # print(dp1)
            dp2[i] = min(dp1[i-1]*nums[i], dp2[i-1]*nums[i], nums[i])
            res = max(res, dp1[i], dp2[i])
        # print(dp1, dp2)
        return res

208 实现 Trie (前缀树) (medium)

前缀树:相关的内容还没有看过,随后用单独的章节来整理相关的问题,系统学习一下

221 最大正方形 (medium)

https://leetcode-cn.com/problems/maximal-square/

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        '''
        动态规划,利用dp[i][j]表示到以matrix[i][j]为正方形右下角元素,最大正方形边长
        状态转移:
        dp[i][j] = min(dp[i-1][j], dp[i-1,j-1], dp[i][j-1]) + 1 if matrix[i][j] == '1'
        
        边界条件:当边界值为1时dp[0][:] = 1, dp[:][0] = 1,否则二者为0
        '''
        if not matrix: return 0
        m = len(matrix)
        n = len(matrix[0])
        dp = [[0]*n for _ in range(m)]
        res = 0
        for i in range(m):
            if matrix[i][0] == '1':
                dp[i][0] = 1
            res = max(res, dp[i][0])
        for j in range(n):
            if matrix[0][j] == '1':
                dp[0][j] = 1
                res = max(res, dp[0][j])
        # print(dp)  
        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][j] == '0':
                    # print(i,j)
                    dp[i][j] = 0
                    continue
                dp[i][j] = min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]) + 1
                res = max(res, dp[i][j])
        # print(dp)
        return res**2

238 除自身以外数组的乘积 (medium)

https://leetcode-cn.com/problems/product-of-array-except-self/

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        '''
        要在常数的时间复杂度完成,且不能使用除法
        计算当前元素左边各个元素的乘积与当前元素右边各个元素的乘积二者相乘即可得到答案
        '''
        L = [1] * len(nums)  # 左边各个元素的乘积
        R = [1] * len(nums)  # 右边各个元素的乘积

        for i in range(1, len(nums)):
            L[i] =L[i-1] * nums[i-1]
        for i in range(len(nums)-2, -1, -1):
            R[i] = R[i+1] * nums[i+1]
        # print(L)
        # print(R)
        return [L[i]*R[i] for i in range(len(nums))]

github地址

更多文章请点击查看

你可能感兴趣的:(leetcode刷题)