LeetCode 71-90题

本博客记录的是 LeetCode 71 到 90 题的题解

之前很少使用的语法


# 71. Simplify Path
class Solution:
    def simplifyPath(self, path: str) -> str:
        new_dir = []
        for name in path.split('/'):
            if name not in ['.', '..', '']:
                new_dir.append(name)
            if name == '..':
                if new_dir:
                    new_dir.pop()
        return '/' + '/'.join(new_dir)

# 72. Edit Distance
a += ' '
a = ' ' + a
f[i][j] = min(f[i - 1][j - 1], f[i - 1][j], f[i][j - 1]) + 1


# 78. Subsets
n, res = len(nums), []
        for i in range(1 << n):
            tmp_res = []
            for j in range(n):
                if (i >> j) & 1:
                    tmp_res.append(nums[j])
            res.append(tmp_res)
        return res
# 85. Maximal Rectangle
b = [item[:] for item in a]

71. Simplify Path

双指针算法

class Solution:
    def simplifyPath(self, path: str) -> str:
        i, j = 0, 0
        n = len(path)
        dir_names = []
        while i < n:
            if path[i] == '/':
                while i < n and path[i] == '/':
                    i += 1
            else:
                j = i + 1
                while j < n and path[j] != '/':
                    j += 1
                dir_names.append(path[i:j])
                i = j

        new_dir = []
        for name in dir_names:
            if name == '.':
                continue
            elif name == '..':
                if new_dir:
                    new_dir.pop()
            else:
                new_dir.append(name)
        
        return '/' + '/'.join(new_dir)
        

利用库函数的精简代码

class Solution:
    def simplifyPath(self, path: str) -> str:
        new_dir = []
        for name in path.split('/'):
            if name not in ['.', '..', '']:
                new_dir.append(name)
            if name == '..':
                if new_dir:
                    new_dir.pop()
        return '/' + '/'.join(new_dir)

72. Edit Distance

入门动态规划,最短编辑距离

# Dp
class Solution:
    def minDistance(self, a: str, b: str) -> int:
        # define and initialize
        n, m = len(a), len(b)
        a = ' ' + a
        b = ' ' + b
        f = [[0] * (m + 1) for i in range(n + 1)]
        for j in range(0, m + 1):
            f[0][j] = j
        for i in range(0, n + 1):
            f[i][0] = i
        
        # dynamic programming
        for i in range(1, n + 1):
            for j in range(1, m + 1):
                if a[i] == b[j]:
                    f[i][j] = f[i - 1][j - 1]
                else:
                    f[i][j] = min(f[i - 1][j - 1], min(f[i - 1][j], f[i][j - 1])) + 1
        return f[n][m]

73. Set Matrix Zeroes

一定遇到原地操作,空间复杂度O(0)的时候,主要是应用好原本的数组,仅仅开辟常数级别的变量

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        flag_0_row, flag_0_column = False, False
        n, m = len(matrix), len(matrix[0])
        for i in range(n):
            for j in range(m):
                if matrix[i][j] == 0:
                    matrix[i][0] = 0
                    matrix[0][j] = 0
                    if i == 0:
                        flag_0_row = True
                    if j == 0:
                        flag_0_column = True
        for i in range(1, n):
            if matrix[i][0] == 0:
                for j in range(m):
                    matrix[i][j] = 0
        for j in range(1, m):
            if matrix[0][j] == 0:
                for i in range(n):
                    matrix[i][j] = 0
        if flag_0_row:
            for j in range(m):
                matrix[0][j] = 0
        if flag_0_column:
            for i in range(n):
                matrix[i][0] = 0

74. Search a 2D Matrix

简单的二分问题,先 row 二分,然后 col 二分即可,查找是否存在 target 数值

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        n, m = len(matrix), len(matrix[0])
        l, r = 0, n - 1
        if matrix[l][0] > target:
            return False
        else:
            # 寻找 matrix[XXX][0] <= target 的最大值
            while l < r:
                mid = (l + r + 1) // 2
                if matrix[mid][0] <= target:
                    l = mid
                else:
                    r = mid - 1
            
            # 寻找 matrix[x][XXX] <= target 的最大值
            x = l
            l, r = 0, m - 1
            while l < r:
                mid = (l + r + 1) // 2
                if matrix[x][mid] <= target:
                    l = mid
                else:
                    r = mid - 1
            return matrix[x][l] == target

也可以直接一维判断,不过好手动转化为二维数组

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        n, m = len(matrix), len(matrix[0])
        l, r = 0, n * m - 1
        while l <= r:
            mid = (l + r) // 2
            if matrix[mid // m][mid % m] == target:
                return True
            elif matrix[mid // m][mid % m] > target:
                r = mid - 1
            else:
                l = mid + 1
        return False

75. Sort Colors

# 快排

# 或者是直接进行数数
class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        cnt, n = [0] * 3, len(nums)
        for i in range(n):
            cnt[nums[i]] += 1
        sum = 0
        for k in range(3):
            for i in range(sum, sum + cnt[k]):
                nums[i] = k
            sum += cnt[k]
            


如果是只想扫描一遍的话,可以按照双指针算法来写
有 0 就从前往后放,有 2 就从后往前放,剩下的就是 1的地方,不过这样放置的话,可能会覆盖一个数组单元,因此需要那一个临时变量存储一下子,这里我使用的 t = nums[n - 1] 来防止 nums[[n - 1]未遍历前就被覆盖了

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        i, j = 0, n - 2
        t = nums[n - 1]
        cnt = 0
        pos_0, pos_2 = 0, n - 1
        flag = True
        while i <= j:
            if flag:
                if nums[i] == 0:
                    nums[pos_0] = 0
                    pos_0 += 1
                elif nums[i] == 2:
                    nums[pos_2] = 2
                    pos_2 -= 1
                    flag = False
                i += 1
            else:
                if nums[j] == 0:
                    nums[pos_0] = 0
                    pos_0 += 1
                    flag = True
                elif nums[j] == 2:
                    nums[pos_2] = 2
                    pos_2 -= 1
                j -= 1
        
        if t == 0:
            nums[pos_0] = 0
            pos_0 += 1
        elif t == 2:
            nums[pos_2] = 2
            pos_2 -= 1
        for i in range(pos_0, pos_2 + 1):
            nums[i] = 1

前面两个是自己想着玩的,看到别人有一个做法,看起来和第二种类似。
首先对于数字2,也是放在后面,不过选择了 swap,也就是说便利时候,只看了 i指针的增加。
并且针对 0、1分界的问题,也是通过 swap 解决

图示

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        i, j, k = 0, -1, n # i 指向的是当前遍历的点,j指向的是 第一个 1所存储的位置
        while i != k:
            if nums[i] == 0:
                nums[j + 1], nums[i] = nums[i], nums[j + 1]
                j += 1
                i += 1
            elif nums[i] == 2:
                nums[i], nums[k - 1] = nums[k - 1], nums[i]
                k -= 1
            else:
                i += 1
            

76. Minimum Window Substring

使用双指针算法,有点类似于滑动区间的感觉
i指针负责前面移动,j指针负责尾部移动, i += 1 之后,查看 j 是否可以往前移动,这需要当前字符串字典的打表,查看是否满足移动的要求

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        # 统计 t
        sum_cnt = {}
        for item in t:
            if item in sum_cnt:
                sum_cnt[item] += 1
            else:
                sum_cnt[item] = 1
        

        # 查看 s
        i, j = 0, 0
        res_st, res_ed = -1, -1
        target = len(sum_cnt)
        cur_num = 0
        n = len(s)
        cur_cnt = {}

        while i < n:
            if s[i] in sum_cnt:
                # 更新 cur_cnt[s[i]]
                if s[i] not in cur_cnt:
                    cur_cnt[s[i]] = 1
                else:
                    cur_cnt[s[i]] += 1
                
                if cur_cnt[s[i]] == sum_cnt[s[i]]:
                    cur_num += 1

            # 查看 j 是否可以后退
            while j <= i and (s[j] not in sum_cnt or cur_cnt[s[j]] > sum_cnt[s[j]]):
                if s[j] in sum_cnt:
                    cur_cnt[s[j]] -= 1
                j += 1
            
            # 更新 i 指针
            i += 1
            # print(f'i={i}, j={j}, cur_num={cur_num}')
            # print(cur_cnt)
            # 更新答案
            if cur_num == target and (res_ed - res_st >= i - j or res_ed == -1):
                res_ed, res_st = i, j
                
        print(f'st={res_st}, ed={res_ed}')
        if res_ed == -1:
            return ''
        else:
            return s[res_st:res_ed] 

python 的 dict 在处理这里有些慢,这里我们尝试使用 Counter

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        # 统计 t
        need_cnt = Counter()
        for item in t:
            need_cnt[item] += 1
        
        # 查看 s
        target = len(need_cnt)
        cur_num = 0
        i, j = 0, 0
        res_st, res_ed = -1, -1
        n = len(s)
        while i < n:
            if s[i] in need_cnt:
                need_cnt[s[i]] -= 1
                if need_cnt[s[i]] == 0:
                    cur_num += 1
            i += 1
            while j < i and (s[j] not in need_cnt or need_cnt[s[j]] < 0):
                if s[j] in need_cnt:
                    need_cnt[s[j]] += 1
                j += 1
            if cur_num == target and (res_ed == -1 or res_ed - res_st > i - j):
                res_ed, res_st = i, j
            
        if res_ed == -1:
            return ''
        else:
            return s[res_st:res_ed] 

77. Combinations

DFS

class Solution:
    tmp_res = []
    res = []

    def dfs(self, cur_pos, cur_num, k, n):
        if cur_pos == k + 1:
            self.res.append(self.tmp_res[:])
            return
        for i in range(cur_num, n + 1):
            if n + 1 - i < k - cur_pos + 1:
                break
            self.tmp_res.append(i)
            self.dfs(cur_pos + 1, i + 1, k, n)
            self.tmp_res.pop()
        
    def combine(self, n: int, k: int) -> List[List[int]]:
        self.tmp_res, self.res = [], []
        self.dfs(1, 1, k, n)

        return self.res

78. Subsets

DFS

class Solution:
    res, tmp_res = [], []

    def dfs(self, cur, nums):
        if cur == len(nums):
            self.res.append(self.tmp_res[:])
            return
        else:
            self.dfs(cur + 1, nums)
            self.tmp_res.append(nums[cur])
            self.dfs(cur + 1, nums)
            self.tmp_res.pop()
            
    def subsets(self, nums: List[int]) -> List[List[int]]:
        self.res, self.tmp_res = [], []
        self.dfs(0, nums)
        return self.res

二进制

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        n, res = len(nums), []
        for i in range(1 << n):
            tmp_res = []
            for j in range(n):
                if (i >> j) & 1:
                    tmp_res.append(nums[j])
            res.append(tmp_res)
        return res

还是得 DFS, BFS 发现不够用

DFS 代码,发现执行用时太长,考虑DP

class Solution:
    st = []
    word = ''
    a = []
    dx, dy = [1, 0, -1, 0], [0, 1, 0, -1]
    def dfs(self, x, y, cur_len):
        if cur_len == len(self.word):
            return True
        if x < 0 or y < 0 or x >= len(self.a) or y >= len(self.a[0]) or self.st[x][y]:
            return False
        if self.word[cur_len] == self.a[x][y]:
            self.st[x][y] = True
            for i in range(4):
                nx, ny = x + self.dx[i], y + self.dy[i]
                if self.dfs(nx, ny,cur_len + 1):
                    return True
            self.st[x][y] = False
        return False

    def exist(self, board: List[List[str]], a: str) -> bool:
        n, m = len(board), len(board[0])
        self.a = board
        self.word = a
        self.st = [[False] * m for i in range(n)]   # 不必次次初始化
        for i in range(n):
            for j in range(m):
                if a[0] == board[i][j]:
                    if self.dfs(i, j, 0):
                        return True


        return False    

80. Remove Duplicates from Sorted Array II

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        if len(nums) <= 2:
            return len(nums)
        pre_val, cnt = 10101010, 0
        j, n = 0, len(nums)
        for i in range(0, n):
            if nums[i] == pre_val:
                cnt += 1
                if cnt <= 2:
                    nums[j] = nums[i]
                    j += 1    
            else:
                cnt = 1
                pre_val = nums[i]
                nums[j] = nums[i]
                j += 1
        return j

81. Search in Rotated Sorted Array II

他和Search in Rotated Sorted Array I有个很大的区别,在于二分需要寻找区间性质
前半部分满足一个性质,后半部分满足一个性质,然后可以二分出中间分界点。
但是因为他value 不是 惟一的,前后区间无法彻底满足这个区间特性。因此我们需要暴力一个 for 循环
举例子,前半部分 nums[] >= nums[0], 后半部分 nums[] <= nums[0]
但是因为他们都具有相等的这个属性,没办法找分界点。因此可以将前面的区间等号范围for枚举掉,也可以将后半部分区间的等号部分 枚举掉。
然后就可以折半查找

class Solution:
    def get(self, l, r, target, nums):
        if target < nums[l] or target > nums[r]:    # 不要写 0, -1
            return False
        while l <= r:
            mid = (l + r) >> 1
            # print(f'l={l}, r={r}, mid={mid}, nums[mid]={nums[mid]}')
            if nums[mid] == target:
                return True
            elif nums[mid] > target:
                r = mid - 1
            else:
                l = mid + 1

        return False

    def search(self, nums: List[int], target: int) -> bool:
        # 我们首先应该寻找 nums 中 小于等于 nums[0] 的最大值
        # 但是这个 l, r首先进行 确定 l 的数值
        l, n = 1, len(nums)
        while l < n and nums[l] == nums[0]:
            l += 1
        if l >= n:  # 全为 nums[0]
            return nums[0] == target
        else:
            if nums[-1] > nums[0]:  # 数组整个为递增
                return self.get(0, len(nums) - 1, target, nums)

            r = len(nums) - 1
            while l < r:
                mid = (l + r) >> 1
                if nums[mid] <= nums[0]:
                    r = mid
                else:
                    l = mid + 1
            # print(f'l={l}, nums[l]={nums[l]}')
            if nums[-1] >= target:
                return self.get(l, len(nums) - 1, target, nums)
            else:
                return self.get(0, l - 1, target, nums)

82. Remove Duplicates from Sorted List II

就是一个指针,不过自己创建一个头结点dummy 会方便很多

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        
        dummy = ListNode(1011, head)
        pre_val = head.val
        p0, p1, p2 = dummy, head, head.next
        while p2:
            if p2.val == pre_val:
                while p2 and p2.val == pre_val:
                    p2 = p2.next
                p0.next = p2
                p1 = p2
                if p2:
                    pre_val = p2.val
                    p2 = p2.next
            else:
                pre_val = p2.val
                p0, p1 = p1, p2
                p2 = p2.next
        return dummy.next

83. Remove Duplicates from Sorted List

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head:
            return head
        p = head.next
        pre_p = head
        while p:
            if p.val != pre_p.val:
                pre_p.next = p
                pre_p = p
                p = p.next
            else:
                pre_p.next = None # 应当给他绝掉
                p = p.next
        return head

84. Largest Rectangle in Histogram

85. Maximal Rectangle

本来以为是 DP,然后一想发现直接统计个数好像就可以
(x1, y1), (x2, y2)
可以通过枚举 x1, y1, x2,然后二分查找合适的 y2

class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        def get(x1, y1, x2, y2):
            return b[x2][y2] - (b[x2][y1-1] if y1 else 0) -\
                (b[x1-1][y2] if x1 else 0) + (b[x1-1][y1-1] if x1 and y1 else 0)

        n, m = len(matrix), len(matrix[0])
        a = [[1 if matrix[i][j] == '1' else 0 for j in range(m)] for i in range(n)]
        b = [item[:] for item in a]
        for i in range(n):
            for j in range(m):
                b[i][j] = b[i][j] + (b[i-1][j] if i else 0) + (b[i][j-1] if j else 0) - (b[i-1][j-1] if i and j else 0)
        
        
        res = 0
        for x1 in range(n):
            for y1 in range(m):
                if a[x1][y1]:
                    if get(x1, y1, n - 1, m - 1) == (n - 1 - x1 + 1) * (m - 1 - y1 + 1):
                        res = max(res, (n - 1 - x1 + 1) * (m - 1 - y1 + 1))
                        break
                    for x2 in range(x1, n):
                        if a[x2][y1] == 0:  # 加速一下子
                            break
                        l, r = y1, m - 1
                        while l < r:
                            mid = (l + r + 1) >> 1
                            if a[x2][mid] and get(x1, y1, x2, mid) == (x2 - x1 + 1) * (mid - y1 + 1):
                                l = mid
                            else:
                                r = mid - 1
                        # print(f'({x1}, {y1}), ({x2}, {l})')
                        res = max(res, (x2 - x1 + 1) * (l - y1 + 1))
        return res
posted @ 2022-01-12 21:38  lucky_light  阅读(61)  评论(0编辑  收藏  举报