Array Questions Part 3
56. (LC 1) Two Sum
(Easy) Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. (You may assume that each input would have exactly one solution, and you may not use the same element twice.)
### V my
def two_sum(a, t):
d = {}
for i, n in enumerate(a):
if t - n in d:
return i, d[t - n]
d[n] = i
return False
nums = [2, 7, 11, 15]
print(two_sum(nums, 9)) # (1, 0)
### Solution 1
def two_sum(array, target):
dic = {}
for i, num in enumerate(array):
if num in dic:
return dic[num], i
else:
dic[target - num] = i
return None
57. (LC 15) 3Sum
15. 3Sum (Medium)
Solution 2 (Sorting + pointers) Internally uses Two Sum II - Input Array Is Sorted.
Here we are not given a sorted input. We sort before proceeding to the algorithm. (LC Topics hints that we should use sorting + two pointers.)
Keys:
# Visualization
# [-1,0,1,2,-1,-4] sort ->
# [-4,-1,-1,0,1,2] use pointers ->
# [-4,-1,-1,0,1,2]
# cur L R
My V3 (Solution2, dropping extra optimizations, the core algorithm. Just for understanding. Not efficient enough for Leetcode.)
def f(a):
if len(a) < 3:
return []
a.sort()
ans = []
for i, v in enumerate(a):
lp = i + 1
rp = len(a) - 1
while lp < rp:
s = v + a[lp] + a[rp]
if s > 0:
rp -= 1
elif s < 0:
lp += 1
else:
res = [v, a[lp], a[rp]]
res.sort()
if res not in ans:
ans.append(res)
lp += 1
rp -= 1
return ans
My V2 (Solution2 with edge cases. Leetcode accepted.)
class Solution:
def threeSum(self, a: List[int]) -> List[List[int]]:
a.sort()
ans = []
if len(a) < 3:
return []
if len(a) == 3:
return [a] if sum(a) == 0 else []
for i in range(len(a) - 2):
if a[i] > 0:
break
if i > 0 and a[i] == a[i - 1]:
continue
cur = a[i]
lp = i + 1
rp = len(a) - 1
while lp < rp:
s = sum([cur, a[lp], a[rp]])
if s < 0:
lp += 1
elif s > 0:
rp -= 1
else:
ans.append([cur, a[lp], a[rp]])
lp += 1
rp -= 1
while a[lp] == a[lp - 1] and lp < rp:
lp += 1
return ans
Solution 2 explained
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
for i, a in enumerate(nums):
# Skip positive integers
if a > 0: #0
break
if i > 0 and a == nums[i - 1]: #1
continue
l, r = i + 1, len(nums) - 1 #2
while l < r:
threeSum = a + nums[l] + nums[r]
if threeSum > 0:
r -= 1
elif threeSum < 0:
l += 1
else:
res.append([a, nums[l], nums[r]])
l += 1
r -= 1
while nums[l] == nums[l - 1] and l < r: #3
l += 1
return res
#0 If current number (a) is positive, then all the following nums are positive as well (sorted array), and we won’t sum them to 0.
#2 3 nums = Our current value and two pointers:
# [-4,-1,-1,0,1,2]
# a L R
# [-1,-1,-1,0,3..]
# a L
If we move L+=1, it will move to the same value, so we move L till it is a different value, or L meets R.
class Solution(object):
def threeSum(self, nums):
# edge cases
if not nums or len(nums) < 3:
return []
if len(nums) == 3:
return [nums] if sum(nums) == 0 else []
if nums.count(0) == len(nums):
return [[0,0,0]]
res = []
nums.sort()
for i in range(len(nums)):
cur = nums[i]
# 2 sum
d = {}
for j, x in enumerate(nums[i+1:]):
# cur + x + y = 0
# -> y = -x - cur
if -x-cur in d:
tmp = [cur, x, -x-cur]
tmp.sort()
if tmp not in res:
res.append(tmp)
else:
d[x] = j
return res
nums = [-1,0,1,2,-1,-4]
print(threeSum(nums)) # [[-1, 0, 1], [-1, -1, 2]]
My V (Brute force, use Python std lib.)
from itertools import combinations
def sums_to_zero(a):
# Out of all combinations with size 3, choose those that sum to 0.
combos = [c for c in combinations(a, 3) if sum(c) == 0]
# Choose only unique combinations
ans = []
for c in combos:
c = list(c)
c.sort()
if c not in ans:
ans.append(c)
return ans
nums = [-1, 0, 1, 2, -1, -4]
print(sums_to_zero(nums)) #[[-1, 0, 1], [-1, -1, 2]]
58. (LC 16) 3Sum Closest
16. 3Sum Closest (Medium)
# <----|
# [-4,-1,1,2]
# R
-if threeSum < Less than target, move MidPoint
# |--->
# [-4,-1,1,2]
# M
Solutions:
### My V3 (LC accepted, 16, 74%)
def threeSumClosest(a, t):
a.sort()
ans = 0
dif = float("inf")
for lp in range(len(a) - 2):
rp = len(a) - 1
mp = lp + 1
while mp < rp:
summing = sum([a[lp], a[mp], a[rp]])
cur_dif = abs(summing - t)
if cur_dif < dif:
dif = cur_dif
ans = summing
if summing > t:
rp -= 1
elif summing < t:
mp += 1
else:
return t
return ans
# My V (Stdlib)
import itertools as it
def sum_closest(a, t):
combos = it.combinations(a, 3)
sums = [sum(c) for c in combos]
ans = sums[0]
dif = abs(t - ans)
for s in sums:
if abs(t - s) < dif:
ans = s
return ans
nums = [-1, 2, 1, -4]
target = 1
print(sum_closest(nums, target)) # 2
Solutions Time: O(n^2):
### 1
def threeSumClosest(nums, target):
N = len(nums)
nums.sort()
res = float('inf') # sum of 3 numbers
for t in range(N):
i, j = t + 1, N - 1
while i < j:
_sum = nums[t] + nums[i] + nums[j]
if abs(_sum - target) < abs(res - target):
res = _sum
if _sum > target:
j -= 1
elif _sum < target:
i += 1
else:
return target
return res
### 2 (pretty much the same)
def threeSumClosest(num, target):
num.sort()
mindiff = 100000
res = 0
for i in range(len(num)):
left = i + 1
right = len(num) - 1
while left < right:
sum = num[i] + num[left] + num[right]
diff = abs(sum - target)
if diff < mindiff:
mindiff = diff
res = sum
if sum == target:
return sum
elif sum < target:
left += 1
else:
right -= 1
return res
59. (LC 989) Add to Array-Form of Integer
(Easy) The array-form of an integer num is an array representing its digits in left to right order. For example, for num = 1321, the array form is [1,3,2,1]. Given num, the array-form of an integer, and an integer k, return the array-form of the integer num + k.
### My v
def add_to_array(a, n):
a = [0] + a
for i in range((len(a) - 1), -1, -1):
a[i] = a[i] + (n % 10) #4+(181%10)=4+1=5
a[i - 1] += a[i] // 10 #7+5//10, i.e. +carry
a[i] = a[i] % 10 #if there was carry on a[i], chop it off
n = n // 10 #chop of right digit from 181, leaving 18
if a[0] == 0:
return a[1:]
return a
num = [2, 7, 4]
k = 181
print(add_to_array(num, k)) #[4, 5, 5]
### Solution 1
# (operation on array)
class Solution:
def addToArrayForm(self, num: List[int], k: int) -> List[int]:
s = ""
for i in num:
s += str(i)
answer = int(s) + k
return list("".join(str(answer))) #why not list(str(answer))
# Using list comprehension
class Solution:
def addToArrayForm(self, A: List[int], K: int) -> List[int]:
return [int(x) for x in str(int(''.join(str(x) for x in A))+K)]
divmod(a,b)
>>> divmod(26, 5)
(5, 1)
### Solution 2
class Solution:
def addToArrayForm(self, num: List[int], k: int) -> List[int]:
i, carry = len(num) - 1, 0
ans = []
while i >= 0 or k or carry:
carry += (0 if i < 0 else num[i]) + (k % 10)
carry, v = divmod(carry, 10)
ans.append(v)
k //= 10
i -= 1
return ans[::-1]
>>> divmod(4, 10)
(0, 4)
>>> divmod(18, 10)
(1, 8)
### Solution 3
class Solution:
def addToArrayForm(self, num: List[int], k: int) -> List[int]:
for i in reversed(range(len(num))):
k, num[i] = divmod(num[i] + k, 10)
while k > 0:
num = [k % 10] + num
k //= 10
return num
60. (LC 419) Battleships in a Board
class Solution(object):
def countBattleships(self, board):
"""
:type board: List[List[str]]
:rtype: int
"""
h = len(board)
w = len(board[0]) if h else 0
ans = 0
for x in range(h):
for y in range(w):
if board[x][y] == 'X':
if x > 0 and board[x - 1][y] == 'X': #if there is a ship above
continue
if y > 0 and board[x][y - 1] == 'X': #if there is a sip to the left
continue
ans += 1
return ans
61. (LC 121) Best Time to Buy and Sell Stock
121. Best Time to Buy and Sell Stock (Easy)
In short: buy and sell once, return max profit.
You are given an array prices where prices[i] is the price of a given stock on the ith day. You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.
Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.
Example 1: Input: prices = [7,1,5,3,6,4] Output: 5 Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5. Note that buying on day 2 and selling on day 1 is not allowed because you must buy before you sell.
Example 2: Input: prices = [7,6,4,3,1] Output: 0 Explanation: In this case, no transactions are done and the max profit = 0.
### My V
def buy_sell(a):
max_pofit, min_price = 0, a[0]
for p in a:
min_price = min(min_price, p)
max_pofit = max(max_pofit, p - min_price)
return max_pofit
### Solution 1
class Solution(object):
def maxProfit(self, prices):
if len(prices) == 0:
return 0
### NOTE : we define 1st minPrice as prices[0]
minPrice = prices[0]
maxProfit = 0
### NOTE : we only loop prices ONCE
for p in prices:
# only if p < minPrice, we get minPrice
if p < minPrice:
minPrice = p
### NOTE : only if p - minPrice > maxProfit, we get maxProfit
elif p - minPrice > maxProfit:
maxProfit = p - minPrice
return maxProfit
### Other Solutions
class Solution:
def maxProfit(self, prices: List[int]) -> int:
ans, mi = 0, inf
for v in prices:
ans = max(ans, v - mi)
mi = min(mi, v)
return ans
class Solution(object):
# @param prices, a list of integers
# @return an integer
def maxProfit(self, prices):
max_profit, min_price = 0, float("inf")
for price in prices:
min_price = min(min_price, price)
max_profit = max(max_profit, price - min_price)
return max_profit
62. (LC 309) Best Time to Buy and Sell Stock with Cooldown
309. Best Time to Buy and Sell Stock with Cooldown (Medium)
# 1
class Solution:
def maxProfit(self, prices: List[int]) -> int:
sell = 0
hold = -math.inf
prev = 0
for price in prices:
cache = sell
sell = max(sell, hold + price)
hold = max(hold, prev - price)
prev = cache
return sell
# 2
class Solution:
def maxProfit(self, prices: List[int]) -> int:
f, f0, f1 = 0, 0, -prices[0]
for x in prices[1:]:
f, f0, f1 = f0, max(f0, f1 + x), max(f1, f - x)
return f0
# 3 Dynamic programming, O(n) [10]:
from typing import List
def maxProfit(prices: List[int]) -> int:
# State: Buying or Selling?
# If Buy -> i + 1
# If Sell -> i + 2 # +2 because +cooldown day
dp = {} # key=(i, buying) val=max_profit, dp implements cashing
def dfs(i, buying):
if i >= len(prices):
return 0
if (i, buying) in dp:
return dp[(i, buying)]
cooldown = dfs(i + 1, buying)
if buying:
buy = dfs(i + 1, not buying) - prices[i]
dp[(i, buying)] = max(buy, cooldown)
else:
sell = dfs(i + 2, not buying) + prices[i]
dp[(i, buying)] = max(sell, cooldown)
return dp[(i, buying)]
return dfs(0, True)
prices = [1, 2, 3, 0, 2]
print(maxProfit(prices))
63. (LC 122) Best Time to Buy and Sell Stock II
122. Best Time to Buy and Sell Stock II (Medium)
Solution 1 [2]
### Solution 1
from typing import List
import itertools
def maxProfit(prices: List[int]) -> int:
return sum(max(0, b - a) for a, b in itertools.pairwise(prices))
prices = [7,1,5,3,6,4]
print(maxProfit(prices)) # 7
itertools.pairwise(iterable)
Solution 2 [10]
### Solution 2
class Solution:
def maxProfit(self, prices: List[int]) -> int:
max_profit = 0
for i in range(1, len(prices)):
if prices[i] > prices[i-1]:
max_profit += prices[i] - prices[i-1]
return max_profit
My V (LC accepted 50, 70%)
class Solution:
def maxProfit(self, prices: List[int]) -> int:
cur_min = prices[0]
total_profit = 0
for price in prices:
cur_min = min(cur_min, price)
profit = price - cur_min
if profit > 0:
cur_min = price
total_profit += profit
return total_profit
Emulating as close as possible the classic buy-sell stock.
64. (LC 1014) Best Sightseeing Pair
1014. Best Sightseeing Pair (Medium)
### Solution 1
class Solution:
def maxScoreSightseeingPair(self, A: List[int]) -> int:
n = len(A)
pre = A[0] + 0
res = 0
for i in range(1, n):
res = max(res, pre + A[i] - i)
pre = max(pre, A[i] + i)
return res
# The same (breaking down the steps)
from typing import List
def f(A: List[int]) -> int:
n = len(A)
pre = A[0] + 0
res = 0
for i in range(1, n):
cur_res = pre + A[i] - i
res = max(res, cur_res)
possible_pre = A[i] + i
pre = max(pre, possible_pre)
return res
res = max(res, pre + A[i] - i)
res = max(res, pre + A[i] - i)
pre = max(pre, A[i] + i)
65. (LC 605) Can Place Flowers
Given an integer array flowerbed containing 0’s and 1’s, where 0 means empty and 1 means not empty, and an integer n, return true if n new flowers can be planted in the flowerbed without violating the no-adjacent-flowers rule and false otherwise.
### My V
def can_plant(a, n):
a = [0] + a + [0]
cnt = 0
for i in range(1, len(a) - 1):
if not a[i] & 1:
if a[i - 1] == 0 and a[i + 1] == 0:
cnt += 1
a[i] = 1
return cnt >= n
flowerbed = [1, 0, 0, 0, 1]
print(can_plant(flowerbed, 1)) # True
print(can_plant(flowerbed, 2)) # False
### Solution 1
class Solution:
def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
flowerbed = [0] + flowerbed + [0]
for i in range(1, len(flowerbed) - 1):
if sum(flowerbed[i - 1 : i + 2]) == 0:
flowerbed[i] = 1
n -= 1
return n <= 0
### Solution 2
class Solution(object):
def canPlaceFlowers(self, flowerbed, n):
"""
:type flowerbed: List[int]
:type n: int
:rtype: bool
"""
for i, num in enumerate(flowerbed):
if num == 1: continue
if i > 0 and flowerbed[i - 1] == 1: continue
if i < len(flowerbed) - 1 and flowerbed[i + 1] == 1: continue
flowerbed[i] = 1
n -= 1
return n <= 0
### Solution 3
class Solution(object):
def canPlaceFlowers(self, flowerbed, n):
"""
:type flowerbed: List[int]
:type n: int
:rtype: bool
"""
flowerbed = [0] + flowerbed + [0]
N = len(flowerbed)
res = 0
for i in range(1, N - 1):
if flowerbed[i - 1] == flowerbed[i] == flowerbed[i + 1] == 0:
res += 1
flowerbed[i] = 1
return res >= n