Leetcode Day3 (数独和矩阵专题)

今天做一个数独和矩阵专题

对python语法的新理解:

  • rows = [[0] * 9] * 9
    这样会导致改动其中一行, 其他行都会跟着改变, 正确的做法是
    rows = [[0] * 9 for _ in range(9)]
  • 想创建一个3x3x9的数组的话,
[[[0 for _ in range(9)] for _ in range(3)] for _ in range(3)]

得反着来, 一步步加框
[0 for _ in range(dim[-1])]
=> [[0 for _ in range(dim[-1])] for _ in range(dim[-2])]
=> [[[0 for _ in range(dim[-1])] for _ in range(dim[-2])] for _ in range(dim[-3])]

好题有:

  • 54: 这种题描述简单, 做起来却不简单

36 数独成立吗(可以有空)

第一想法: 直接遍历, 每一行, 每一列, 每一3x3都保存一个哈希表(直接数组就可以了)
时间: O ( 81 ) O(81) O(81) 空间: O ( 81 ∗ 3 ) O(81 * 3) O(813)

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        rows = [[0] * 9 for _ in range(9)]
        cols = [[0] * 9 for _ in range(9)]
        grids = [[[0 for _ in range(9)] for _ in range(3)] for _ in range(3)]
        
        for i in range(9):
            for j in range(9):
                num = board[i][j]
                if num == ".":
                    continue
                num = int(num) - 1
                if rows[i][num] != 0:
                    return False
                else:
                    rows[i][num] = 1
                
                if cols[j][num] != 0:
                    return False
                else:
                    cols[j][num] = 1
                
                x = i // 3
                y = j // 3
                if grids[x][y][num] != 0:
                    return False
                else: 
                    grids[x][y][num] = 1
        
        return True

惊喜的是能过
看了题解发现一个很有趣的, 对于grid我采用的是三维数组可能有点麻烦, 但我看别人直接用 i / 3 + ( j / 3 ) ∗ 3 i/3 + (j/3)*3 i/3+(j/3)3, 这样一个二维数组就搞定了

37 解数独

解数独的难度直接直线上升了, 放回溯专题好了

54 顺时针螺旋顺序 ,返回矩阵中的所有元素

from typing import List

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        m = len(matrix)
        n = len(matrix[0])
        res = []
        directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        direction = 0  # 开始时向右
        pos = [0, 0]   # 初始位置
        visited = [[False for _ in range(n)] for _ in range(m)]
        
        for _ in range(m * n):
            i, j = pos
            res.append(matrix[i][j])
            visited[i][j] = True
            
            # 计算下一个位置
            next_i, next_j = i + directions[direction][0], j + directions[direction][1]
            
            # 检查是否需要转向:如果超出边界或下一个位置已经访问过
            if not (0 <= next_i < m and 0 <= next_j < n and not visited[next_i][next_j]):
                direction = (direction + 1) % 4
                next_i, next_j = i + directions[direction][0], j + directions[direction][1]
            
            pos = [next_i, next_j]
        
        return res

遇到了一个很强的方法, 我一开始的想法是, 既然第一排遍历完, 后面就用不上了, 那我能不能遍历完一排, 逆时针旋转90度再删,直到没有.
但这个方法当然时间复杂度就上去了, 其实这个方法的核心就是逐渐缩小边界, 当然你不一定要用旋转并删除啦, 所以一个很优美的不用记录已访问数据的方法是

from typing import List

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        # 记录上下左右边界
        m = len(matrix)
        n = len(matrix[0])
        l = 0 
        r = n - 1
        u = 0
        d = m - 1
        res = []
        while (len(res) < m * n):
            # 向右遍历
            for i in range(l, r + 1):
                res.append(matrix[u][i])
            u+=1
            # 向下    
            for i in range(u , d + 1):
                res.append(matrix[i][r])
            r-=1
            # 向左
            for i in range(r, l - 1, -1):
                res.append(matrix[d][i])
            d -=1
            # 向上
            for i in range(d, u - 1, -1):
                res.append(matrix[i][l])
            l+=1
        return res[:m*n]

48 顺时针旋转正方形矩阵(原地, 不能用其他矩阵)

直接能想到先转置, 再每行反过来

for i in range(len(matrix)):
    for j in range(i, len(matrix)):
        temp = matrix[i][j]
        matrix[i][j] = matrix[j][i]
        matrix[j][i] = temp
for row in matrix:
    row = row.reverse()

但我犯了个很傻的错,

for j in range(len(matrix))

这不翻转了两次吗

73 把0元素所在的列和行都设为0

难点是空间只能 O ( 1 ) O(1) O(1)
O ( m n ) O(mn) O(mn)就是最直接的算法,
O ( m + n ) O(m+n) O(m+n)记录哪些行哪些列
但大家都在骂出的题, 我也觉得没啥意义, 先不管吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值