Skip to content

Commit

Permalink
feat(python): solutions
Browse files Browse the repository at this point in the history
  • Loading branch information
hongbo-miao committed May 31, 2022
1 parent 5598e22 commit 0bc3d9b
Show file tree
Hide file tree
Showing 12 changed files with 632 additions and 8 deletions.
4 changes: 2 additions & 2 deletions Python/0020. Valid Parentheses.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ def isValid(self, s: str) -> bool:
if c in dic:
st.append(dic[c])
else:
if len(st) == 0 or st.pop() != c:
if not st or st.pop() != c:
return False
return len(st) == 0
return not st
74 changes: 74 additions & 0 deletions Python/0133. Clone Graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Given a reference of a node in a connected undirected graph.
# Return a deep copy (clone) of the graph.
# Each node in the graph contains a value (int) and a list (List[Node]) of its neighbors.
#
# class Node {
# public int val;
# public List<Node> neighbors;
# }
#
#
# Test case format:
#
# For simplicity, each node's value is the same as the node's index (1-indexed). For example, the first node with val == 1, the second node with val == 2, and so on. The graph is represented in the test case using an adjacency list.
# An adjacency list is a collection of unordered lists used to represent a finite graph. Each list describes the set of neighbors of a node in the graph.
# The given node will always be the first node with val = 1. You must return the copy of the given node as a reference to the cloned graph.
#
# Example 1:
#
# Input: adjList = [[2,4],[1,3],[2,4],[1,3]]
# Output: [[2,4],[1,3],[2,4],[1,3]]
# Explanation: There are 4 nodes in the graph.
# 1st node (val = 1)'s neighbors are 2nd node (val = 2) and 4th node (val = 4).
# 2nd node (val = 2)'s neighbors are 1st node (val = 1) and 3rd node (val = 3).
# 3rd node (val = 3)'s neighbors are 2nd node (val = 2) and 4th node (val = 4).
# 4th node (val = 4)'s neighbors are 1st node (val = 1) and 3rd node (val = 3).
#
# Example 2:
#
# Input: adjList = [[]]
# Output: [[]]
# Explanation: Note that the input contains one empty list. The graph consists of only one node with val = 1 and it does not have any neighbors.
#
# Example 3:
#
# Input: adjList = []
# Output: []
# Explanation: This an empty graph, it does not have any nodes.
#
# Constraints:
#
# The number of nodes in the graph is in the range [0, 100].
# 1 <= Node.val <= 100
# Node.val is unique for each node.
# There are no repeated edges and no self-loops in the graph.
# The Graph is connected and all nodes can be visited starting from the given node.


"""
# Definition for a Node.
class Node:
def __init__(self, val = 0, neighbors = None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
"""


# DFS
class Solution:
def cloneGraph(self, node: "Node") -> "Node":
if not node:
return None

cache = {}

def clone(u0):
if u0 in cache:
return cache[u0]
u1 = Node(u0.val)
cache[u0] = u1
for v in u0.neighbors:
u1.neighbors.append(clone(v))
return u1

return clone(node)
76 changes: 76 additions & 0 deletions Python/0153. Find Minimum in Rotated Sorted Array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become:
#
# [4,5,6,7,0,1,2] if it was rotated 4 times.
# [0,1,2,4,5,6,7] if it was rotated 7 times.
# Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]].
#
# Given the sorted rotated array nums of unique elements, return the minimum element of this array.
#
# You must write an algorithm that runs in O(log n) time.
#
# Example 1:
#
# Input: nums = [3,4,5,1,2]
# Output: 1
# Explanation: The original array was [1,2,3,4,5] rotated 3 times.
#
# Example 2:
#
# Input: nums = [4,5,6,7,0,1,2]
# Output: 0
# Explanation: The original array was [0,1,2,4,5,6,7] and it was rotated 4 times.
#
# Example 3:
#
# Input: nums = [11,13,15,17]
# Output: 11
# Explanation: The original array was [11,13,15,17] and it was rotated 4 times.
#
# Constraints:
#
# n == nums.length
# 1 <= n <= 5000
# -5000 <= nums[i] <= 5000
# All the integers of nums are unique.
# nums is sorted and rotated between 1 and n times.


# 1) Binary Search
class Solution:
def findMin(self, nums: List[int]) -> int:
if len(nums) == 1:
return nums[0]

l = 0
r = len(nums) - 1

# e.g. 1 < 2 < 3 < 4 < 5 sorted array, so return nums[0]
if nums[r] > nums[0]:
return nums[0]

while l <= r:
m = (l + r) // 2

if nums[m - 1] > nums[m]:
return nums[m]
if nums[m] > nums[m + 1]:
return nums[m + 1]

if nums[m] > nums[0]:
l = m + 1
else:
r = m - 1


# 2) Binary Search
class Solution:
def findMin(self, nums: List[int]) -> int:
l = 0
r = len(nums) - 1
while l < r:
m = (l + r) // 2
if nums[m] > nums[r]:
l = m + 1
else:
r = m
return nums[l]
4 changes: 2 additions & 2 deletions Python/0188. Best Time to Buy and Sell Stock IV.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@

# 1) Dynamic Programming (Top-Down)
# https://leetcode.com/explore/learn/card/dynamic-programming/632/common-patterns-in-dp-problems/4116/
from functools import lru_cache
from functools import cache


class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
@lru_cache(None)
@cache
def dp(i, transactions_remaining, holding):
# Base case
if transactions_remaining == 0 or i == len(prices):
Expand Down
5 changes: 4 additions & 1 deletion Python/0276. Paint Fence.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@ def total_ways(i):
# 2) Dynamic Programming (Top-Down). Similar to 1)
# Time O(n)
# Space O(n)
from functools import cache


class Solution:
def numWays(self, n: int, k: int) -> int:
@lru_cache(None)
@cache
def total_ways(i):
if i == 1:
return k
Expand Down
51 changes: 51 additions & 0 deletions Python/0286. Walls and Gates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# You are given an m x n grid rooms initialized with these three possible values.
#
# -1 A wall or an obstacle.
# 0 A gate.
# INF Infinity means an empty room. We use the value 231 - 1 = 2147483647 to represent INF as you may assume that the distance to a gate is less than 2147483647.
#
# Fill each empty room with the distance to its nearest gate. If it is impossible to reach a gate, it should be filled with INF.
#
# Example 1:
#
# Input: rooms = [[2147483647,-1,0,2147483647],[2147483647,2147483647,2147483647,-1],[2147483647,-1,2147483647,-1],[0,-1,2147483647,2147483647]]
# Output: [[3,-1,0,1],[2,2,1,-1],[1,-1,2,-1],[0,-1,3,4]]
#
# Example 2:
#
# Input: rooms = [[-1]]
# Output: [[-1]]
#
# Constraints:
#
# m == rooms.length
# n == rooms[i].length
# 1 <= m, n <= 250
# rooms[i][j] is -1, 0, or 2^31 - 1.

# BFS
class Solution:
def wallsAndGates(self, rooms: List[List[int]]) -> None:
"""
Do not return anything, modify rooms in-place instead.
"""
if not rooms:
return

dirs = [(0, 1), (0, -1), (1, 0), (-1, 0)]
m, n = len(rooms), len(rooms[0])

q = []
for i in range(m):
for j in range(n):
if rooms[i][j] == 0:
q.append((i, j))

while q:
i, j = q.pop(0)
for di, dj in dirs:
x, y = i + di, j + dj
if 0 <= x < m and 0 <= y < n and rooms[x][y] == 2147483647:
rooms[x][y] = rooms[i][j] + 1
q.append((x, y))
return
61 changes: 61 additions & 0 deletions Python/0346. Moving Average from Data Stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window.
# Implement the MovingAverage class:
#
# - MovingAverage(int size) Initializes the object with the size of the window size.
# - double next(int val) Returns the moving average of the last size values of the stream.
#
# Example 1:
#
# Input
# ["MovingAverage", "next", "next", "next", "next"]
# [[3], [1], [10], [3], [5]]
# Output
# [null, 1.0, 5.5, 4.66667, 6.0]
#
# Explanation
# MovingAverage movingAverage = new MovingAverage(3);
# movingAverage.next(1); // return 1.0 = 1 / 1
# movingAverage.next(10); // return 5.5 = (1 + 10) / 2
# movingAverage.next(3); // return 4.66667 = (1 + 10 + 3) / 3
# movingAverage.next(5); // return 6.0 = (10 + 3 + 5) / 3
#
# Constraints:
#
# 1 <= size <= 1000
# -105 <= val <= 105
# At most 104 calls will be made to next.


# 1) Array
# Time O(N) where N is the size of the moving window, since we need to retrieve NN elements from the queue at each invocation of next(val) function.
# Space O(M), where M is the length of the queue which would grow at each invocation of the next(val) function.
class MovingAverage:
def __init__(self, size: int):
self.size = size
self.q = []

def next(self, val: int) -> float:
self.q.append(val)
# calculate the sum of the moving window
window_sum = sum(self.q[-self.size :])
return window_sum / min(len(self.q), self.size)


# 2) Double-ended Queue
# Time O(1), as we explained in intuition.
# Space O(N), where NN is the size of the moving window.
class MovingAverage:
def __init__(self, size: int):
self.size = size
self.q = []
# number of elements seen so far
self.window_sum = 0
self.count = 0

def next(self, val: int) -> float:
self.count += 1
# calculate the new sum by shifting the window
self.q.append(val)
tail = self.q.pop(0) if self.count > self.size else 0
self.window_sum = self.window_sum - tail + val
return self.window_sum / min(self.size, self.count)
60 changes: 60 additions & 0 deletions Python/0494. Target Sum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# You are given an integer array nums and an integer target.
# You want to build an expression out of nums by adding one of the symbols '+' and '-' before each integer in nums and then concatenate all the integers.
# For example, if nums = [2, 1], you can add a '+' before 2 and a '-' before 1 and concatenate them to build the expression "+2-1".
# Return the number of different expressions that you can build, which evaluates to target.
#
# Example 1:
#
# Input: nums = [1,1,1,1,1], target = 3
# Output: 5
# Explanation: There are 5 ways to assign symbols to make the sum of nums be target 3.
# -1 + 1 + 1 + 1 + 1 = 3
# +1 - 1 + 1 + 1 + 1 = 3
# +1 + 1 - 1 + 1 + 1 = 3
# +1 + 1 + 1 - 1 + 1 = 3
# +1 + 1 + 1 + 1 - 1 = 3
#
# Example 2:
#
# Input: nums = [1], target = 1
# Output: 1
#
# Constraints:
#
# 1 <= nums.length <= 20
# 0 <= nums[i] <= 1000
# 0 <= sum(nums[i]) <= 1000
# -1000 <= target <= 1000

# 1) DFS
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
def find(i, t):
if (i, t) in cache:
return cache[(i, t)]
count = 0
if i == len(nums):
if t == 0:
count = 1
else:
count = find(i + 1, t - nums[i]) + find(i + 1, t + nums[i])
cache[(i, t)] = count
return count

cache = {}
return find(0, target)


# 2) DFS, similar to 1)
from functools import cache


class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
@cache
def find(i, t):
if i == len(nums):
return 1 if t == 0 else 0
return find(i + 1, t - nums[i]) + find(i + 1, t + nums[i])

return find(0, target)
Loading

0 comments on commit 0bc3d9b

Please sign in to comment.