diff --git a/problem28.py b/problem28.py new file mode 100644 index 00000000..edad1d69 --- /dev/null +++ b/problem28.py @@ -0,0 +1,23 @@ +class Solution: + def productExceptSelf(self, nums: List[int]) -> List[int]: + result = [0]*len(nums) + #take the running sum of products. For an integer nums[i] take left running sum and then find its right running sum and multiply them both together. + + #taking the left side running sum + rp = 1 + + for i,num in enumerate(nums): + result[i] = rp + rp = num*rp + + #taking the right side running sum + rp = 1 + + for i in range (len(nums)-1, -1, -1): + result[i] *= rp + rp *= nums[i] + + return result + +# Time Complexity: O(N) +# Space Complexity: O(1) if you don't consider the output array, else O(N) \ No newline at end of file diff --git a/problem29.py b/problem29.py new file mode 100644 index 00000000..18f7d4eb --- /dev/null +++ b/problem29.py @@ -0,0 +1,104 @@ +#my code initial thought process: + +from typing import List + + +class Solution: + def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]: + result = [] + m = len(mat) #rows + n = len(mat[0]) #cols + + if m == 1 and n == 1: + return [mat[0][0]] + + top = True + + i = 0 + j = 0 + + while top and i >= 0 and i < m and j >=0 and j=0 and j < n: + result.append(mat[i][j]) + i = i-1 + j = j+1 + + if j > n-1: + i += 2 + j = j-1 + else: + i = 0 + + top = False + + #downwards left and bottom + while (j >= 0 and i < m) and not top: + result.append(mat[i][j]) + i = i+1 + j = j-1 + + if i > m-1: + i = i-1 + j = j+2 + + else: + j = 0 + top = True + return result + + +# up-right diagonals don’t always end by hitting the top wall. +# Sometimes they end by hitting the right wall (j == n). In that case, resetting i = 0 is wrong. +# Down-left diagonals sometimes end at the left wall (j == -1) and sometimes at the bottom wall (i == m), and each needs a different fix. + +#more clean code: + +class Solution: + def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]: + result = [] + m = len(mat) #rows + n = len(mat[0]) #cols + + row = 0 + col = 0 + dir = True + + #find all cases where we need to reverse directions + + while row < m and col < n: + result.append(mat[row][col]) #always add the first element + + if dir: #for upward traversals boundaries are roof and right so when we hit boundaries + if row == 0 and col != n-1: #roof boundary is reached as roof boundary is row = zero + col += 1 + dir = False + elif col == n-1: #right boundary is reached as right boundary = col n-1 and the corner case is also reached + row += 1 + dir = False + else: + row -= 1 #general cases in upward dir when we are not hitting boundaries + col += 1 + + else: #for downward traversal left and bottom are boundaries + if row == m-1: #hit bottom boundary and corner case is handled here + col += 1 + dir = True + + elif col == 0 and row != m-1: #hit left boundary or the corner case + row += 1 + dir = True + else: + #generally + col -= 1 + row += 1 + return result + +#we need to handle the intersection of two boundaries or the corner cases here as it can also go OOB +#when to decide i have to flip the directions +#in which direction we have to flip +#if we are on (0,0) how to decide which index to go to. + +# Time Complexity : O(m*n) +# Space Complexity: O(1) (O(m*n) if u want to consider result also) \ No newline at end of file diff --git a/problem30.py b/problem30.py new file mode 100644 index 00000000..c2853ae6 --- /dev/null +++ b/problem30.py @@ -0,0 +1,97 @@ +from typing import List + + +class Solution: + def spiralOrder(self, matrix: List[List[int]]) -> List[int]: + + #top bottom left and right defines the boundaries and after each spiral, these boundaries + #changes + + m = len(matrix) #rows + n = len(matrix[0]) #cols + result = [] + + top, left, right, bottom = 0, 0, n - 1, m - 1 + + while top <= bottom and left <= right: + + for i in range(left, right+1): + result.append(matrix[top][i]) + + top += 1 + + for i in range(top, bottom+1): #if top > bottom then this for loop won't run + result.append(matrix[i][right]) + + right -= 1 + + if top <= bottom: #if base condition var gets mutated always check them again + for i in range(right, left-1, -1): + result.append(matrix[bottom][i]) + + bottom -= 1 + + if left <= right: + for i in range(bottom, top-1, -1): + result.append(matrix[i][left]) + left += 1 + + + return result + +# Time Complexity: O(m*n) +# Space Complexity: O(1) + +class Solution: + def spiralOrder(self, matrix: List[List[int]]) -> List[int]: + + #top bottom left and right defines the boundaries and after each spiral, these boundaries + #changes + + m = len(matrix) #rows + n = len(matrix[0]) #cols + result = [] + + top, left, right, bottom = 0, 0, n - 1, m - 1 + + def helper(matrix, top, left, right, bottom, result): + + if top > bottom or left > right: #That’s the key reason recursion depth is min(m, n). Stop when either dimensionality is + #exhausted + return + + for i in range(left, right+1): + result.append(matrix[top][i]) + + top += 1 + + for i in range(top, bottom+1): + result.append(matrix[i][right]) + right -= 1 + + if top <= bottom: + for i in range(right, left-1, -1): + result.append(matrix[bottom][i]) + + bottom -= 1 + + if left <= right: + for i in range(bottom, top-1, -1): + result.append(matrix[i][left]) + + left += 1 + + helper(matrix, top, left, right, bottom,result) + + helper (matrix, top, left, right, bottom, result) + + return result + +# Recursive solution: + +# Time Complexity: O(m*n) +# Space Complexity: recursive stack depends no. of spirals +# O(min(m, n)) +# because each recursive call removes one spiral layer, and the number of layers is bounded by the smaller matrix dimension. +# After one recursive call, the matrix shrinks like this (m-2) rows*(n-2) cols. Each call reduces both dimensions. +#ceil(min(m, n) / 2) = 3, basically whichever exhausts first either row or col.