class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        # time O(RC) space O(RC)
        if not matrix:
            return []
        R,C = len(matrix),len(matrix[0])
        # here is to optimize 
        #if not C:
        #    return res 
        seen = [[False]*C for _ in range(R)]
        res = []
        dd = [(0,1),(1,0),(0,-1),(-1,0)]
        r = c = di = 0
        for _ in range(R*C):
            res.append(matrix[r][c])
            seen[r][c] = True
            ry, cx = r+dd[di][0], c+dd[di][1]
            if 0 <= ry < R and  0 <= cx < C and not seen[ry][cx]:
                r,c = ry, cx
            else:
                di = (di+1) %4
                r, c = r+dd[di][0], c+dd[di][1]
        return res 

optimization

# main idea: set up,right, bottom, left boundry in clockwise order. each direction iteration complete, narrow down boundry by 1 and check if current condition violate the boundry.
class Solution:
# time O(N) space O(1)
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix or not matrix[0] :
            return []
        res = []
        t,b,l,r = 0,len(matrix)-1,0,len(matrix[0])-1
        dir = 0
        while l<= r and t <= b:
            # top :left ->right 
            if(dir == 0):
                for j in range(l,r+1):
                    res.append(matrix[t][j])
                t += 1
            if(dir == 1):
        # rigth: top to bottom 
                for i in range(t,b+1):
                    res.append(matrix[i][r])
                r -= 1
            if(dir == 2):
      # bottom:  right to left 
                for j in range(r,l-1,-1):
                    res.append(matrix[b][j])
                b -= 1
            if(dir == 3):
                # left : bottom -> up
                for i in range(b,t-1,-1):
                    res.append(matrix[i][l])
                l += 1
            dir = (dir+1)%4
        return res