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 Jun 5, 2022
1 parent e2d3662 commit 63ced2c
Show file tree
Hide file tree
Showing 12 changed files with 411 additions and 25 deletions.
7 changes: 7 additions & 0 deletions Python/0013. Roman to Integer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
# C 100
# D 500
# M 1000
#
# For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II.
#
# Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used:
Expand Down Expand Up @@ -46,9 +47,15 @@ class Solution:
def romanToInt(self, s: str) -> int:
dic = {"M": 1000, "D": 500, "C": 100, "L": 50, "X": 10, "V": 5, "I": 1}
n = 0

# Loop through the string without the last character
for i in range(len(s) - 1):
# e.g. IV
if dic[s[i]] < dic[s[i + 1]]:
n -= dic[s[i]]
# e.g. VI, II
else:
n += dic[s[i]]

# Add the last one
return n + dic[s[-1]]
112 changes: 111 additions & 1 deletion Python/0015. 3Sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# -10^5 <= nums[i] <= 10^5


# Three pointers
# 1) 3 pointers
# https://www.youtube.com/watch?v=y-zBV7uUkyI
#
# Idea
Expand Down Expand Up @@ -71,3 +71,113 @@ def threeSum(self, nums: List[int]) -> List[List[int]]:
while l < r and nums[r] == c:
r -= 1
return res


# 2) 3 pointers, similar to 1)
# Time O(n^2). twoSumII is O(n), and we call it n times.
# Sorting the array takes O(nlogn), so overall complexity is O(nlogn+n^2
# This is asymptotically equivalent to (n^2).
# Space O(logn) to O(n), depending on the implementation of the sorting algorithm.
# For the purpose of complexity analysis, we ignore the memory required for the output.
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
for i in range(len(nums)):
if nums[i] > 0:
break
if i == 0 or nums[i - 1] != nums[i]:
self.twoSum(nums, i, res)
return res

def twoSum(self, nums: List[int], target: int, res: List[List[int]]):
l, r = target + 1, len(nums) - 1
while l < r:
sum = nums[target] + nums[l] + nums[r]
if sum < 0:
l += 1
elif sum > 0:
r -= 1
else:
res.append([nums[target], nums[l], nums[r]])
l += 1
r -= 1
while l < r and nums[l] == nums[l - 1]:
l += 1


# 3) Hashset
# Time O(n^2). twoSum is O(n), and we call it nn times.
# Sorting the array takes O(nlogn), so overall complexity is O(nlogn + n^2)
# This is asymptotically equivalent to O(n^2).
# Space O(n) for the hashset.
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
for i in range(len(nums)):
if nums[i] > 0:
break
if i == 0 or nums[i - 1] != nums[i]:
self.twoSum(nums, i, res)
return res

def twoSum(self, nums: List[int], target: int, res: List[List[int]]):
seen = set()
j = target + 1
while j < len(nums):
diff = -nums[target] - nums[j]
if diff in seen:
res.append([nums[target], nums[j], diff])
while j + 1 < len(nums) and nums[j] == nums[j + 1]:
j += 1
seen.add(nums[j])
j += 1


# 4) kSum
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
def kSum(nums: List[int], target: int, k: int) -> List[List[int]]:
res = []

# If we have run out of numbers to add, return res.
if not nums:
return res

# There are k remaining values to add to the sum.
# The average of these values is at least target // k.
average_value = target // k

# We cannot obtain a sum of target
# if the smallest value in nums is greater than target // k
# or if the largest value in nums is smaller than target // k.
if nums[0] > average_value or nums[-1] < average_value:
return res

if k == 2:
return twoSum(nums, target)

for i in range(len(nums)):
if i == 0 or nums[i - 1] != nums[i]:
for subset in kSum(nums[i + 1 :], target - nums[i], k - 1):
res.append([nums[i]] + subset)
return res

def twoSum(nums: List[int], target: int) -> List[List[int]]:
res = []
l, r = 0, len(nums) - 1
while l < r:
sm = nums[l] + nums[r]
if sm < target or (l > 0 and nums[l] == nums[l - 1]):
l += 1
elif sm > target or (r < len(nums) - 1 and nums[r] == nums[r + 1]):
r -= 1
else:
res.append([nums[l], nums[r]])
l += 1
r -= 1
return res

nums.sort()
return kSum(nums, 0, 3)
44 changes: 44 additions & 0 deletions Python/0016. 3Sum Closest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Given an integer array nums of length n and an integer target, find three integers in nums such that the sum is closest to target.
# Return the sum of the three integers.
# You may assume that each input would have exactly one solution.
#
# Example 1:
#
# Input: nums = [-1,2,1,-4], target = 1
# Output: 2
# Explanation: The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
#
# Example 2:
#
# Input: nums = [0,0,0], target = 1
# Output: 0
#
# Constraints:
#
# 3 <= nums.length <= 1000
# -1000 <= nums[i] <= 1000
# -10^4 <= target <= 10^4


# 3 pointers
# Time Complexity: O(n^2). We have outer and inner loops, each going through nn elements.
# Sorting the array takes O(nlogn), so overall complexity is O(nlogn + n^2).
# This is asymptotically equivalent to O(n^2).
# Space from O(logn) to O(n), depending on the implementation of the sorting algorithm.
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
diff = float("inf")
nums.sort()
for i in range(len(nums)):
l, r = i + 1, len(nums) - 1
while l < r:
sum = nums[i] + nums[l] + nums[r]
if abs(target - sum) < abs(diff):
diff = target - sum
if sum < target:
l += 1
else:
r -= 1
if diff == 0:
break
return target - diff
88 changes: 68 additions & 20 deletions Python/0018. 4Sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@
# -10^9 <= nums[i] <= 10^9
# -10^9 <= target <= 10^9

# Notion

# 1) Two Pointers
# Time O(n^{k - 1}), or O(n^3) for 4Sum. We have k - 2 loops, and twoSum is O(n).
# Note that for k > 2k>2, sorting the array does not change the overall time complexity.
# Space O(n). We need O(k) space for the recursion. k can be the same as nn in the worst case for the generalized algorithm.
# Note that, for the purpose of complexity analysis, we ignore the memory required for the output.
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
def kSum(nums: List[int], target: int, k: int) -> List[List[int]]:
Expand All @@ -31,14 +37,14 @@ def kSum(nums: List[int], target: int, k: int) -> List[List[int]]:
if not nums:
return res

# There are k remaining values to add to the sum. The
# average of these values is at least target // k.
# There are k remaining values to add to the sum.
# The average of these values is at least target // k.
average_value = target // k

# We cannot obtain a sum of target if the smallest value
# in nums is greater than target // k or if the largest
# value in nums is smaller than target // k.
if average_value < nums[0] or nums[-1] < average_value:
# We cannot obtain a sum of target
# if the smallest value in nums is greater than target // k
# or if the largest value in nums is smaller than target // k.
if nums[0] > average_value or nums[-1] < average_value:
return res

if k == 2:
Expand All @@ -48,26 +54,68 @@ def kSum(nums: List[int], target: int, k: int) -> List[List[int]]:
if i == 0 or nums[i - 1] != nums[i]:
for subset in kSum(nums[i + 1 :], target - nums[i], k - 1):
res.append([nums[i]] + subset)

return res

def twoSum(nums: List[int], target: int) -> List[List[int]]:
res = []
lo, hi = 0, len(nums) - 1

while lo < hi:
curr_sum = nums[lo] + nums[hi]
if curr_sum < target or (lo > 0 and nums[lo] == nums[lo - 1]):
lo += 1
elif curr_sum > target or (
hi < len(nums) - 1 and nums[hi] == nums[hi + 1]
):
hi -= 1
l, r = 0, len(nums) - 1
while l < r:
sm = nums[l] + nums[r]
if sm < target or (l > 0 and nums[l] == nums[l - 1]):
l += 1
elif sm > target or (r < len(nums) - 1 and nums[r] == nums[r + 1]):
r -= 1
else:
res.append([nums[lo], nums[hi]])
lo += 1
hi -= 1
res.append([nums[l], nums[r]])
l += 1
r -= 1
return res

nums.sort()
return kSum(nums, target, 4)


# 2) Hash Set
# Time O(n^{k - 1}), or O(n^3) for 4Sum. We have k - 2 loops iterating over nn elements, and twoSum is O(n).
# Note that for k > 2, sorting the array does not change the overall time complexity.
# Space O(n) for the hash set. The space needed for the recursion will not exceed O(n).
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
def kSum(nums: List[int], target: int, k: int) -> List[List[int]]:
res = []

# If we have run out of numbers to add, return res.
if not nums:
return res

# There are k remaining values to add to the sum.
# The average of these values is at least target // k.
average_value = target // k

# We cannot obtain a sum of target
# if the smallest value in nums is greater than target // k
# or if the largest value in nums is smaller than target // k.
if nums[0] > average_value or nums[-1] < average_value:
return res

if k == 2:
return twoSum(nums, target)

for i in range(len(nums)):
if i == 0 or nums[i - 1] != nums[i]:
for subset in kSum(nums[i + 1 :], target - nums[i], k - 1):
res.append([nums[i]] + subset)

return res

def twoSum(nums: List[int], target: int) -> List[List[int]]:
res = []
s = set()
for i in range(len(nums)):
if len(res) == 0 or res[-1][1] != nums[i]:
if target - nums[i] in s:
res.append([target - nums[i], nums[i]])
s.add(nums[i])
return res

nums.sort()
Expand Down
5 changes: 3 additions & 2 deletions Python/0054. Spiral Matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
res = []
while steps[dir % 2] > 0:
for _ in range(steps[dir % 2]):
x += dirs[dir][0]
y += dirs[dir][1]
dx, dy = dirs[dir]
x += dx
y += dy
res.append(matrix[x][y])
steps[dir % 2] -= 1
dir = (dir + 1) % 4
Expand Down
32 changes: 32 additions & 0 deletions Python/0076. Minimum Window Substring.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,37 @@
# Follow up: Could you find an algorithm that runs in O(m + n) time?


# Notion

# Sliding window
import collections


class Solution:
def minWindow(self, s: str, t: str) -> str:
need = collections.Counter(t) # hash table to store char frequency
missing = len(t) # total number of chars we care
start, end = 0, 0
l = 0
# Move the right pointer until the window contains all the chars from string t
for r, c in enumerate(s, 1):
if need[c] > 0:
missing -= 1
need[c] -= 1

# Now the window has all chars
if missing == 0:
# Remove chars to find the real start
while l < r and need[s[l]] < 0:
need[s[l]] += 1
l += 1

# Update window
if end == 0 or r - l < end - start:
start, end = l, r

# Move left pointer again which makes the window no more desirable to find next window
need[s[l]] += 1
missing += 1
l += 1
return s[start:end]
1 change: 1 addition & 0 deletions Python/0125. Valid Palindrome.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@
class Solution:
def isPalindrome(self, s: str) -> bool:
s = s.lower()
# isalnum() -> True if all chars are alphanumeric (alphabet + number)
s = "".join(c for c in s if c.isalnum())
return s == s[::-1]
4 changes: 3 additions & 1 deletion Python/0227. Basic Calculator II.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@
# 5. Now, operation is equal to *, so we pop last element from stack and put -3\4*5 instead, stack = [1*2, -3\4*5].
# 6. Finally, operation is equal to +, so we put 6 to stack: stack = [1*2, -3\4*5, 6]
# Now, all we need to do is to return sum of all elements in stack.


class Solution:
def calculate(self, s):
def calculate(self, s: str) -> int:
i = 0
n = 0
st = []
Expand Down
2 changes: 1 addition & 1 deletion Python/0239. Sliding Window Maximum.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

# Notion

# 1) Monotonic Queue
# Monotonic Queue
# https://www.youtube.com/watch?v=2SXqBsTR6a8
#
# Time O(n)
Expand Down
Loading

0 comments on commit 63ced2c

Please sign in to comment.