Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DSA by Python #45

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Algorithms/UniqueBinarySearchTrees/numTrees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def numTrees(self, n):
if n == 1: return 1
res = [1, 1]
for i in range(2, n + 1):
val = 0
for j in range(i):
val += res[j] * res[i - j - 1]
res.append(val)
return res[n]
24 changes: 24 additions & 0 deletions Algorithms/UniqueBinarySearchTreesII/generateTrees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
def generateTrees(self, n):
if n == 0: return []

def helper(start, end):
ls = []
if start > end:
ls.append(None)
return ls
if start == end:
ls.append(TreeNode(start))
return ls

for i in range(start, end + 1):
left = helper(start, i - 1)
right = helper(i + 1, end)
for lnode in left:
for rnode in right:
root = TreeNode(i)
root.left = lnode
root.right = rnode
ls.append(root)
return ls

return helper(1, n)
20 changes: 20 additions & 0 deletions Algorithms/ValidMountainArray/validMountainArray.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Method 1: using index find the max first, and then process

def validMountainArray(self, A):
if len(A) < 3: return False
index = A.index(max(A))
if index == 0 or index == len(A) -1: return False
for i in range(1, len(A)):
if i <= index:
if A[i] <= A[i - 1]: return False
else:
if A[i] >= A[i - 1]: return False
return True


# Method 2: one pass, using two pointers trace from the begining and end
def validMountainArray(self, A):
i, j = 0, len(A) - 1
while i < len(A) - 1 and A[i] < A[i + 1]: i += 1
while j > 0 and A[j - 1] > A[j]: j -= 1
return 0 < i == j < len(A) - 1
26 changes: 26 additions & 0 deletions Algorithms/ValidateBinarySearchTree/isValidBST.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# method 1: using recursion

def isValidBST1(self, root, lower = float('-inf'), upper = float('inf')):
"""
:type root: TreeNode
:rtype: bool
"""
if not root: return True
if root.val <= lower or root.val >= upper: return False
return self.isValidBST(root.left, lower, min(upper, root.val)) \
and self.isValidBST(root.right, max(lower, root.val), upper)


# method 2: a proper BST should have this porperty: inorder traversal is increasing
def isValidBST2(self, root):
inorder = []
def helper(root):
if root:
helper(root.left)
inorder.append(root.val)
helper(root.right)

helper(root)
for i in range(len(inorder) - 1):
if inorder[i + 1] <= inorder[i]: return False
return True
19 changes: 19 additions & 0 deletions Algorithms/XOfAKindInADeckOfCards/hasGroupsSizeX.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Method 1: find the greatest common divisor using iteration

def hasGroupsSizeX(self, deck):
if len(deck) < 2: return False
vals = collections.Counter(deck).values()
for n in range(2, max(vals) + 1):
if all(v % n == 0 for v in vals): return True
return False

# Method 2: find the greatest common divisor using reduce
# Time complexity: O(n)
def hasGroupsSizeX(self, deck):
from functools import reduce
if len(deck) < 2: return False
vals = collections.Counter(deck).values()
def gcd(a, b):
while b: a, b = b, a % b
return a
return reduce(gcd, vals)
74 changes: 74 additions & 0 deletions Algorithms/uniquePaths/uniquePathsIII.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# 980. Unique Paths III
# On a 2-dimensional grid, there are 4 types of squares:

# 1 represents the starting square. There is exactly one starting square.
# 2 represents the ending square. There is exactly one ending square.
# 0 represents empty squares we can walk over.
# -1 represents obstacles that we cannot walk over.
# Return the number of 4-directional walks from the starting square to the ending square, that walk over every non-obstacle square exactly once.



# Example 1:

# Input: [[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
# Output: 2
# Explanation: We have the following two paths:
# 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
# 2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
# Example 2:

# Input: [[1,0,0,0],[0,0,0,0],[0,0,0,2]]
# Output: 4
# Explanation: We have the following four paths:
# 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
# 2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
# 3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
# 4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
# Example 3:

# Input: [[0,1],[2,0]]
# Output: 0
# Explanation:
# There is no path that walks over every empty square exactly once.
# Note that the starting and ending square can be anywhere in the grid.


# Note:

# 1 <= grid.length * grid[0].length <= 20

class Solution:
ans = 0
def findPathNum(self, i, j, grid: List[List[int]], curLen, pLen)->None:
if(grid[i][j]==2):
if(pLen-1==curLen):
self.ans+=1
return
elif (grid[i][j]==-1):
return
curLen+=1
grid[i][j]=-1
if(i-1>=0):
self.findPathNum(i-1, j, grid, curLen, pLen)
if(j-1>=0):
self.findPathNum(i, j-1, grid, curLen, pLen)
if(i+1<len(grid)):
self.findPathNum(i+1, j, grid, curLen, pLen)
if(j+1<len(grid[0])):
self.findPathNum(i, j+1, grid, curLen, pLen)
grid[i][j]=0


def uniquePathsIII(self, grid: List[List[int]]) -> int:
pathLen = 0
start = (0, 0)
for i in range(len(grid)):
for j in range(len(grid[0])):
if(grid[i][j]!=-1):
pathLen+=1
if(grid[i][j]==1):
start = (i, j)
self.findPathNum(start[0], start[1], grid, 0, pathLen)
return self.ans

9 changes: 9 additions & 0 deletions UniqueBinarySearchTrees/numTrees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def numTrees(self, n):
if n == 1: return 1
res = [1, 1]
for i in range(2, n + 1):
val = 0
for j in range(i):
val += res[j] * res[i - j - 1]
res.append(val)
return res[n]
24 changes: 24 additions & 0 deletions UniqueBinarySearchTreesII/generateTrees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
def generateTrees(self, n):
if n == 0: return []

def helper(start, end):
ls = []
if start > end:
ls.append(None)
return ls
if start == end:
ls.append(TreeNode(start))
return ls

for i in range(start, end + 1):
left = helper(start, i - 1)
right = helper(i + 1, end)
for lnode in left:
for rnode in right:
root = TreeNode(i)
root.left = lnode
root.right = rnode
ls.append(root)
return ls

return helper(1, n)
20 changes: 20 additions & 0 deletions ValidMountainArray/validMountainArray.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Method 1: using index find the max first, and then process

def validMountainArray(self, A):
if len(A) < 3: return False
index = A.index(max(A))
if index == 0 or index == len(A) -1: return False
for i in range(1, len(A)):
if i <= index:
if A[i] <= A[i - 1]: return False
else:
if A[i] >= A[i - 1]: return False
return True


# Method 2: one pass, using two pointers trace from the begining and end
def validMountainArray(self, A):
i, j = 0, len(A) - 1
while i < len(A) - 1 and A[i] < A[i + 1]: i += 1
while j > 0 and A[j - 1] > A[j]: j -= 1
return 0 < i == j < len(A) - 1
26 changes: 26 additions & 0 deletions ValidateBinarySearchTree/isValidBST.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# method 1: using recursion

def isValidBST1(self, root, lower = float('-inf'), upper = float('inf')):
"""
:type root: TreeNode
:rtype: bool
"""
if not root: return True
if root.val <= lower or root.val >= upper: return False
return self.isValidBST(root.left, lower, min(upper, root.val)) \
and self.isValidBST(root.right, max(lower, root.val), upper)


# method 2: a proper BST should have this porperty: inorder traversal is increasing
def isValidBST2(self, root):
inorder = []
def helper(root):
if root:
helper(root.left)
inorder.append(root.val)
helper(root.right)

helper(root)
for i in range(len(inorder) - 1):
if inorder[i + 1] <= inorder[i]: return False
return True
19 changes: 19 additions & 0 deletions XOfAKindInADeckOfCards/hasGroupsSizeX.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Method 1: find the greatest common divisor using iteration

def hasGroupsSizeX(self, deck):
if len(deck) < 2: return False
vals = collections.Counter(deck).values()
for n in range(2, max(vals) + 1):
if all(v % n == 0 for v in vals): return True
return False

# Method 2: find the greatest common divisor using reduce
# Time complexity: O(n)
def hasGroupsSizeX(self, deck):
from functools import reduce
if len(deck) < 2: return False
vals = collections.Counter(deck).values()
def gcd(a, b):
while b: a, b = b, a % b
return a
return reduce(gcd, vals)
74 changes: 74 additions & 0 deletions uniquePaths/uniquePathsIII.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# 980. Unique Paths III
# On a 2-dimensional grid, there are 4 types of squares:

# 1 represents the starting square. There is exactly one starting square.
# 2 represents the ending square. There is exactly one ending square.
# 0 represents empty squares we can walk over.
# -1 represents obstacles that we cannot walk over.
# Return the number of 4-directional walks from the starting square to the ending square, that walk over every non-obstacle square exactly once.



# Example 1:

# Input: [[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
# Output: 2
# Explanation: We have the following two paths:
# 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
# 2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
# Example 2:

# Input: [[1,0,0,0],[0,0,0,0],[0,0,0,2]]
# Output: 4
# Explanation: We have the following four paths:
# 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
# 2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
# 3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
# 4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
# Example 3:

# Input: [[0,1],[2,0]]
# Output: 0
# Explanation:
# There is no path that walks over every empty square exactly once.
# Note that the starting and ending square can be anywhere in the grid.


# Note:

# 1 <= grid.length * grid[0].length <= 20

class Solution:
ans = 0
def findPathNum(self, i, j, grid: List[List[int]], curLen, pLen)->None:
if(grid[i][j]==2):
if(pLen-1==curLen):
self.ans+=1
return
elif (grid[i][j]==-1):
return
curLen+=1
grid[i][j]=-1
if(i-1>=0):
self.findPathNum(i-1, j, grid, curLen, pLen)
if(j-1>=0):
self.findPathNum(i, j-1, grid, curLen, pLen)
if(i+1<len(grid)):
self.findPathNum(i+1, j, grid, curLen, pLen)
if(j+1<len(grid[0])):
self.findPathNum(i, j+1, grid, curLen, pLen)
grid[i][j]=0


def uniquePathsIII(self, grid: List[List[int]]) -> int:
pathLen = 0
start = (0, 0)
for i in range(len(grid)):
for j in range(len(grid[0])):
if(grid[i][j]!=-1):
pathLen+=1
if(grid[i][j]==1):
start = (i, j)
self.findPathNum(start[0], start[1], grid, 0, pathLen)
return self.ans