Array Questions Part 4
66. (LC 1109) Corporate Flight Bookings
1109. Corporate Flight Bookings Medium
My version 1
def corpFlightBookings(bookings: List[List[int]], n: int) -> List[int]:
ans = [0] * n
for first, last, seats in bookings:
ans[first - 1] += seats
while last > first:
ans[last-1] += seats
last -= 1
return ans
bookings = [[1,2,10],[2,3,20],[2,5,25]]
n = 5
print(corpFlightBookings(bookings, n)) # [10, 55, 45, 25, 25]
### V2
def f2(data, n):
ans = [0] * n
for first, last, seats in data:
for i in range(first - 1, last): #again indexing in our ans is first-1
ans[i] += seats
return ans
Solution
def corpFlightBookings(bookings: List[List[int]], n: int) -> List[int]:
ans = [0] * n
for first, last, seats in bookings:
ans[first - 1] += seats
if last < n:
ans[last] -= seats
return list(itertools.accumulate(ans))
bookings = [[1,2,10],[2,3,20],[2,5,25]]
n = 5
print(corpFlightBookings(bookings, n)) # [10, 55, 45, 25, 25]
67. (LC 697) Degree of an Array
### Solution 1 (left, right dicts)
import collections
class Solution(object):
def findShortestSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
counts = collections.Counter(nums)
left, right = {}, {}
for i, num in enumerate(nums):
left.setdefault(num, i)
right[num] = i
degree = max(counts.values())
return min(right[num]-left[num]+1 \
for num in counts.keys() \
if counts[num] == degree)
### My remake of S1, left, right dicts (Final efficiency+readability balance)
import collections
def f(a):
cnt = collections.Counter(a)
max_cnt = max(cnt.values()) # find most frequent count, e.g. 2 times
max_nums = [
k for k, v in cnt.items() if v == max_cnt
] # nums for most freq count, e.g. [1,2]
left, right = {}, {}
for i, n in enumerate(a):
left.setdefault(n, i)
right[n] = i
lengths = []
for num in max_nums:
length = right[num] - left[num] + 1
lengths.append(length)
return min(lengths)
nums = [1, 2, 2, 3, 1]
nums2 = [1, 2, 2, 3, 1, 4, 2]
print(f(nums)) # 2
print(f(nums2)) # 6
### My V (indexing)
import collections
def f(a):
cnt = collections.Counter(a)
max_cnt = max(cnt.values()) # find most frequent count, e.g. 2 times
max_nums = [
k for k, v in cnt.items() if v == max_cnt
] # nums for most freq count, e.g. [1,2]
subarrays = []
for n in max_nums: # for each of the most freq numbers
start = a.index(n)
end = len(a) - a[::-1].index(n) - 1
subarrays.append(len(a[start : end + 1])) # append len of subarray
return min(subarrays)
Tools How do we make the ‘left’ dictionary. To record only the first time we encounter a number.
dict.setdefault(key[, default])
If key is in the dictionary, return its value. If not, insert key with a value of
default and return default. (default defaults to None.)
>>> d = {30:45}
>>> d.setdefault(25, 50) #new key
50
>>> d
{30: 45, 25: 50} #OK, sets new key with value
>>> d.setdefault(25, 60) #key already in dict
50
>>> d
{30: 45, 25: 50} #Not OK, keep the old value
### Solution with "no tricks" (the least efficient for that)
import collections
def f(a):
cnt = collections.Counter(a)
values = [] # Because there can be several values with the same degree
degree = 0
for v in cnt.values(): #OR degree=max(cnt.values())
if v > degree:
degree = v
[values.append(k) for k, v in cnt.items() if v == degree]
ans = []
for value in values:
subarray_len = 0
for n in a:
if n == value:
subarray_len += 1
degree -= 1
elif n != value and degree > 0 and subarray_len > 0:
subarray_len += 1
ans.append(subarray_len)
return min(ans)
68. (LC 498) Diagonal Traverse
498. Diagonal Traverse Medium
Solution:
class Solution:
def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]:
m, n = len(mat), len(mat[0]) #n is matrix width,
ans = []
for k in range(m + n - 1):
t = []
i = 0 if k < n else k - n + 1 #after k>n, i grows +1
j = k if k < n else n - 1 #after k>n, j will be static, =2
while i < m and j >= 0:
t.append(mat[i][j])
i += 1
j -= 1
if k % 2 == 0:
t = t[::-1]
ans.extend(t)
return ans
69. (LC 888) Fair Candy Swap
Solutions
### V3
def candy_swap(A, B):
mid = int((sum(A + B)) / 2)
for n in A:
pair = mid - (sum(A) - n)
if pair in B:
return [n, pair]
### V0
class Solution:
def fairCandySwap(self, aliceSizes: List[int], bobSizes: List[int]) -> List[int]:
diff = (sum(aliceSizes) - sum(bobSizes)) >> 1
s = set(bobSizes)
for a in aliceSizes:
target = a - diff
if target in s:
return [a, target]
Explained
diff = (sum(aliceSizes) - sum(bobSizes)) >> 1
We are ensured that there is a solution, means the total number of candies both
kids have is an even number. Means it is divisible by 2. The most efficient way to divide
by 2 is to remove one LSB from the even number (LSB in even numbers is always 0).
Removing LSB 0 amounts to dividing by 2.
>>> bin(6)
'0b110'
>>> 6 >> 1
3
>>> bin(3)
'0b11'
diff - is rather the num of candies each kid will have, when they both have the same num.
More solutions
### V1
class Solution(object):
def fairCandySwap(self, A, B):
"""
:type A: List[int]
:type B: List[int]
:rtype: List[int]
"""
sum_A, sum_B, set_B = sum(A), sum(B), set(B)
target = (sum_A + sum_B) / 2
for a in A:
b = target - (sum_A - a)
if b >= 1 and b <= 100000 and b in set_B:
return [a, b]
### V2
class Solution(object):
def fairCandySwap(self, A, B):
"""
:type A: List[int]
:type B: List[int]
:rtype: List[int]
"""
diff = (sum(A)-sum(B))//2
setA = set(A)
for b in set(B):
if diff+b in setA:
return [diff+b, b]
return []
70. (LC 442) Find All Duplicates in an Array
442. Find All Duplicates in an Array Medium
### V1
class Solution(object):
def findDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
ans = []
for n in nums:
if nums[abs(n) - 1] < 0:
ans.append(abs(n))
else:
nums[abs(n) - 1] *= -1
return ans
### V0
Note, this doesn't satisfy O(1) space of the task
from collections import Counter
class Solution(object):
def findDuplicates(self, nums):
return [elem for elem, count in Counter(nums).items() if count == 2]
71. (LC 448) Find All Numbers Disappeared in an Array
448. Find All Numbers Disappeared in an Array Easy
### V1
def findDisappearedNumbers2(nums):
return list(set(range(1, len(nums) + 1)) - set(nums))
### V2
def findDisappearedNumbers(self, nums):
n = set(nums)
new = set(range(1, len(nums) + 1))
return list(new - n) # works on "set" data structure
### V my
def find_missing(a):
ans = list(range(1, len(a) + 1))
for i in a:
if i in ans:
ans.remove(i)
return ans
72. (LC 724) Find Pivot Index
# V1
class Solution:
def pivotIndex(self, nums: List[int]) -> int:
left, right = 0, sum(nums)
for i, x in enumerate(nums):
right -= x #(right=(right - value_of_pivot))
if left == right:
return i
left += x #(left + value_of_pivot)
return -1
- Note:
right -= x
# V2
class Solution(object):
def pivotIndex(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
sums = sum(nums)
total = 0
for x, n in enumerate(nums):
if sums - n == 2 * total:
return x
total += n
return -1
# My V1
def find_pivot(a):
a = [0] + a + [0]
p = -1
for i in range(1, len(a) - 1):
sl = sum(a[0:i])
sr = sum(a[i + 1 : len(a)])
if sl == sr:
p = i - 1
return p
nums = [1, 7, 3, 6, 5, 6] #3
nums2 = [2, 1, -1] #0
print(find_pivot(nums))
print(find_pivot(nums2))
# V2
def find_pivot(a):
a = [0] + a + [0]
for i in range(1, len(a) - 1):
if sum(a[0:i]) == sum(a[(i + 1) : len(a)]):
return i - 1
return -1
nums = [1, 7, 3, 6, 5, 6]
print(find_pivot(nums)) #3
# V3
def f(a):
s = sum(a)
for i in range(len(a)):
if i == 0:
s_left = 0
else:
s_left = sum(a[0:i])
if i == len(a) - 1:
s_right = 0
else:
s_right = s - s_left - a[i]
if s_left == s_right:
return i
return -1
73. (LC 1275) Find Winner on a Tic Tac Toe Game
1275. Find Winner on a Tic Tac Toe Game Easy
A catch in creating multidimensional arrays
This type of declaration will not create m*n spaces in memory; rather, only one integer will be created and referenced by each element of the inner list.
>>> grid = [[""] * 3] * 3
>>> grid
[['', '', ''], ['', '', ''], ['', '', '']]
The usual assignment will give you what you expect.
>>> grid[0][0] = 'x'
>>> grid
[['x', '', ''], ['x', '', ''], ['x', '', '']]
Use generator, then elements will be independent.
>>> grid = [[0]*3 for i in range(3)] # Generalizing, [ [0] * c for i in range(r) ]
>>> grid
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> grid[0][0]= 4
>>> grid
[[4, 0, 0], [0, 0, 0], [0, 0, 0]]
# IDEA : RECORD EACH MOVE
def tictactoe(moves):
n = 3 # size of the board
rows, cols = [0] * n, [0] * n
diag = anti_diag = 0
# player A will have value 1, player B value -1, player A starts.
player = 1
for row, col in moves:
# Using data from the given array 'moves' record the move of a player.
rows[row] += player
cols[col] += player
# If this move is placed on diagonal or anti-diagonal,
# we shall update the relative value as well.
if row == col:
diag += player
if row + col == n - 1:
anti_diag += player
# check if this move meets any of the winning conditions.
if any(abs(line) == n for line in (rows[row], cols[col], diag, anti_diag)):
return "A" if player == 1 else "B"
# If no one wins so far, change to the other player.
player *= -1
# If all moves are completed and there is still no result, we shall check if
# the grid is full or not. If so, the game ends with draw, otherwise pending.
return "Draw" if len(moves) == n * n else "Pending"
moves = [[0, 0], [2, 0], [1, 1], [2, 1], [2, 2]]
print(tictactoe(moves)) #A
# The same without comments
def tictactoe(moves):
n = 3
rows, cols = [0] * n, [0] * n
diag = anti_diag = 0
player = 1
for row, col in moves:
rows[row] += player
cols[col] += player
if row == col:
diag += player
if row + col == n - 1:
anti_diag += player
if any(abs(line) == n for line in (rows[row], cols[col], diag, anti_diag)):
return "A" if player == 1 else "B"
player *= -1
return "Draw" if len(moves) == n * n else "Pending"
moves = [[0, 0], [2, 0], [1, 1], [2, 1], [2, 2]]
print(tictactoe(moves)) #A
BRUTE FORCE:
### My V3
def tic_tac(moves):
grid = [[0] * 3 for i in range(3)]
player = 1
for m in moves:
grid[m[0]][m[1]] = player
player *= -1
search1 = find_winner(grid)
rotated_grid = list(list(reversed(x)) for x in zip(*grid))
search2 = find_winner(rotated_grid)
diag1 = diag2 = []
for i in range(3):
diag1.append(grid[i][i])
diag2.append(rotated_grid[i][i])
search3 = calc_sum(diag1)
search4 = calc_sum(diag2)
result = [search1, search2, search3, search4]
if any(result):
for r in result:
if r:
return r
elif len(moves) < 9:
return "Pending"
else:
return "Draw"
def calc_sum(row):
if sum(row) == 3:
winner = "A"
elif sum(row) == -3:
winner = "B"
else:
winner = None
return winner
def find_winner(grid):
for row in grid:
winner = calc_sum(row)
if winner:
return winner
### My V2
def tic_tac_toe(moves):
grid = [[0] * 3 for i in range(3)]
mark = 1
for move in moves:
grid[move[0]][move[1]] = mark
mark *= -1
# print(grid)
winner1 = get_winner(grid)
winner2 = get_winner(list(zip(*grid)))
winner3 = get_winner(get_diagonals(grid))
# print(winner1, winner2, winner3)
winners = [winner1, winner2, winner3]
for w in winners:
if w != None:
return w
if len(moves) < 9:
return "Pending"
else:
return "Draw"
def get_winner(grid):
winner = None
for row in grid:
if sum(row) == 3:
winner = "A"
elif sum(row) == -3:
winner = "B"
return winner
def get_diagonals(grid):
d1 = d2 = []
for i in range(3):
d1.append(grid[i][i])
d2.append(grid[i][abs(i - 2)])
diagonals = [d1, d2]
return diagonals
moves = [[0, 0], [2, 0], [1, 1], [2, 1], [2, 2]]
moves2 = [[0, 0], [2, 0], [1, 1], [2, 1], [1, 2], [2, 2]]
moves3 = [[0, 0], [1, 1], [2, 0], [1, 0], [1, 2], [2, 1], [0, 1], [0, 2], [2, 2]]
print(tic_tac_toe(moves)) # A
print(tic_tac_toe(moves2)) # B
print(tic_tac_toe(moves3)) # Draw
### My V1
def tic_tac_toe(a):
grid = [[0] * 3 for i in range(3)]
for i in range(len(a)):
if i % 2 == 0:
mark = 1
else:
mark = -1
grid[a[i][0]][a[i][1]] = mark
# print(grid)
# print(list(zip(*grid)))
ans1 = find_winner(grid)
ans2 = find_winner(list(zip(*grid))) # transpose grid and do the same check
ans3 = find_winner2(grid) # check winner in diagonals
ans = [ans1, ans2, ans3]
if any(ans):
return ans
if len(a) < 9:
return "Pending"
return "Draw"
def find_winner(grid):
for i in grid:
if sum(i) == 3:
return "A"
elif sum(i) == -3:
return "B"
def find_winner2(grid):
diag1 = diag2 = []
for i in range(3):
diag1.append(grid[i][i])
diag2.append(grid[i][abs(i - 2)])
if sum(diag1) == 3 or sum(diag2) == 3:
return "A"
elif sum(diag1) == -3 or sum(diag2) == -3:
return "B"
moves = [[0, 0], [2, 0], [1, 1], [2, 1], [2, 2]]
moves2 = [[0, 0], [2, 0], [1, 1], [2, 1], [1, 2], [2, 2]]
moves3 = [[0, 0], [1, 1], [2, 0], [1, 0], [1, 2], [2, 1], [0, 1], [0, 2], [2, 2]]
print(tic_tac_toe(moves)) # [None, None, 'A']
print(tic_tac_toe(moves2)) # ['B', None, None]
print(tic_tac_toe(moves3)) # Draw
74. (LC 287) Find the Duplicate Number
287. Find the Duplicate Number Medium
Given an array of integers nums containing n + 1 integers where each integer is in the range [1, n] inclusive. There is only one repeated number in nums, return this repeated number.
You must solve the problem without modifying the array nums and uses only constant extra space.
Example 1: Input: nums = [1,3,4,2,2] Output: 2
Easier solutions
# set
def find_dups(a):
return sum(a) - sum(set(a))
# Counter
from collections import Counter
def find_dups2(a):
return [k for k, v in Counter(a).items() if v > 1]
sum(v <= x for v in nums)
### Solution 1 (using builtin for bin search)
from typing import List
from bisect import bisect_left
def findDuplicate(nums: List[int]) -> int:
def f(x: int) -> bool:
return sum(v <= x for v in nums) > x #*
return bisect_left(range(len(nums)), True, key=f) #**
#* mostly returns False, but for one True
#** for [1,3,4,2,2] , range(0, 5), lookup for [0,1,2,3,4] gives [False, False, True, False..] we search for True, bisect returns index 2. (So the f function takes as arguments nums from the range?)
### Solution 2 (Implement bin search manually)
#V1
class Solution(object):
def findDuplicate(self, nums):
low, high = 1, len(nums) - 1
while low <= high:
mid = (low + high) >> 1
cnt = sum(x <= mid for x in nums)
if cnt > mid:
high = mid - 1
else:
low = mid + 1
return low
#V2
def findDuplicate(nums):
L, R = 1, len(nums) - 1
while L <= R:
mid = (L + R) >> 1
cnt = sum([1 for num in nums if num <= mid])
if cnt <= mid:
L = mid + 1
else:
R = mid - 1
return L
### Solution 3
# IDEA : TWO POINTERS
class Solution(object):
def findDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
slow , fast = nums[0] , nums[nums[0]]
while slow != fast:
slow = nums[slow]
fast = nums[nums[fast]]
slow = 0
while slow != fast:
slow = nums[slow]
fast = nums[fast]
return slow
Not acceptable solutions:
# Using dict lookup, but space
# addresses, but changes given array
def find_dups3(a):
for i in a:
if a[abs(i) - 1] < 0:
return i
a[i - 1] *= -1
# Sort array first, but changes array
class Solution:
def findDuplicate(self, nums):
nums.sort()
for i in range(1, len(nums)):
if nums[i] == nums[i-1]:
return nums[i]
75. (LC 412) Fizz Buzz
412. Fizz Buzz Easy
class Solution(object):
def fizzBuzz(self, n):
"""
:type n: int
:rtype: List[str]
"""
ans = []
for x in range(1, n + 1):
n = str(x)
if x % 15 == 0:
n = "FizzBuzz"
elif x % 3 == 0:
n = "Fizz"
elif x % 5 == 0:
n = "Buzz"
ans.append(n)
return ans