Leetcode: Arrays Flashcards

1
Q

Best time to buy a stock

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.

A
  1. need to find the current minimum value during the first pass
  2. as we find values that are lower than our min, which starts at index 0, we change our min to that one
  3. whenever we find that sell>buy, we do a max calculation
  4. we can accomplish this in 1 loop because we stop once the right pointer (sell index) is greater than the length of the list. USE FOR LOOP FOR THIS

time: O(n)
space: constant

class BestStockPrice(object):

  • —def answer(self, arr):
  • ——-buy=0
  • ——-money=0
  • ——-for i in range(1, len(arr)):
  • ———–if arr[i]>arr[buy]:
  • —————money=max(money, arr[i]-arr[buy])
  • ———–else:
  • —————buy=i
  • ——-return money
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Contains Duplicate

Given an integer array nums, return true if any value appears at least twice in the array, and return false if every element is distinct.

A
  1. for loop + set of seen items, if item in set return true, else false
  2. set() removes duplicates in python. get length of list/set, if len(list)!=len(set), return true (it found a duplicate and removed it) else false

time: linear
space: linear

class ContainsDuplicates(object):

  • —def answer(self, arr):
  • ——-before=len(arr)
  • ——-after=len(set(arr))
  • ——-if after!=before:
  • ———–return True
  • ——-return False
  • —def answer2(self, nums):
  • ——-seen=set()
  • ——-for x in nums:
  • ———–if x in seen:
  • —————return True
  • ———–seen.add(x)
  • ——-return False
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Murder witnesses

Hi, here’s your problem today. This problem was recently asked by Google:

There are n people lined up, and each have a height represented as an integer. A murder has happened right in front of them, and only people who are taller than everyone in front of them are able to see what has happened. How many witnesses are there?

Example:
Input: [3, 6, 3, 4, 1]  
Output: 3
Explanation: Only [6, 4, 1] were able to see in front of them.
-#
-#
-#-#
####
####
#####
36341----------------------------x (murder scene)
A
  1. increment backwards
  2. update the count each time arr[i] > max
  3. set the max each time you iterate, if you want 100% optimal you set the new max each time arr[i]>max. This is because if arr[i]> max, then it is the new max

time: O(n)
space: constant

import sys
class Solution:
----def answer(self, arr):
--------_max=-sys.maxsize-1
--------count=0
--------for i in range(len(arr)-1, -1, -1):
------------if arr[i]>_max:
----------------count+=1
----------------#_max=max(arr[i], _max)
----------------_max=arr[i]
--------return count
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Sort list of 3 unique numbers

Hi, here’s your problem today. This problem was recently asked by Google:

Given a list of numbers with only 3 unique numbers (1, 2, 3), sort the list in O(n) time.

Example 1:
Input: [3, 3, 2, 1, 3, 2, 1]
Output: [1, 1, 2, 2, 3, 3, 3]
def sortNums(nums):
  # Fill this in.
print sortNums([3, 3, 2, 1, 3, 2, 1])
# [1, 1, 2, 2, 3, 3, 3]
A
  1. bubble sort, except we know the min/max values so we can make it linear
  2. need 3 pointers, ones, current, and threes.
  3. threes decrement when hit, ones/twos increment current, ones increments ones
  4. check edge cases [1,2,2], [2,2,2]. [1.3.2], [3,3,3]
  5. threes>=current, we need the equal because if [2,3,1], current=threes but we still need 1 more operation to be correct

time: O(n), we do 1 loop
space: constant, we do not create any additional structures

class Solution:

  • —def answer(self, arr):
  • ——-threes=len(arr)-1
  • ——-current=ones=0
  • ——-while threes>=current:
  • ———–if arr[current]==1:
  • —————arr[ones], arr[current]=arr[current], arr[ones]
  • —————ones+=1
  • —————current+=1
  • ———–elif arr[current]==2:
  • —————current+=1
  • ———–elif arr[current]==3:
  • —————arr[threes],arr[current]=arr[current], arr[threes]
  • —————threes-=1
  • ——-return arr
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Pythagorean triplets

Hi, here’s your problem today. This problem was recently asked by Uber:

Given a list of numbers, find if there exists a pythagorean triplet in that list. A pythagorean triplet is 3 variables a, b, c where a^2 + b^2 = c^2

Example:
Input: [3, 5, 12, 5, 13]
Output: True
Here, 5^2 + 12^2 = 13^2.

A

brute for is n^3 without making a preprocessed set, probably need to do some square root stuff too

  1. if you square the list you get the all the possible c^2’s
  2. make it a set, double for loop to get all the possible a/b combinations
  3. use set to get constant time searching for c in the list

time: O(n^2), we need to loop twice to see all the values
space: O(n) to store the c^2

class Solution:

  • —def answer(self, li):
  • ——-squares = {x**2 for x in li}
  • ——-for i, x in enumerate(li):
  • ———–for y in li[i+1:]:
  • —————if x2+y2 in squares:
  • ——————-return True
  • ——-return False
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

First and last index of string/number

Hi, here’s your problem today. This problem was recently asked by AirBNB:

Given a sorted array, A, with possibly duplicated elements, find the indices of the first and last occurrences of a target element, x. Return -1 if the target is not found.

# Example:
# Input: A = [1,3,3,5,7,8,9,9,9,15], target = 9
# Output: [6,8]
# Input: A = [100, 150, 150, 153], target = 150
# Output: [1,2]
# Input: A = [1,2,3,4,5,6,10], target = 9
# Output: [-1, -1]
A

brute force is O n, loop until you find, continue until the number isn’t the target

  1. binary search, understand what binary search is
  2. this is a variation of it. binary search is usually greater than, less than, or equal to. This combines these depending on if you want the higher index or lower one
  3. for the lower one, need to prioritize moving to the left if it is equal, on equal but not lowest index move left
  4. make sure you understand that the stop>=start at all times, if it is not, return -1
  5. when index==target, make sure that for the right to check if the index+1>current or index==len(list)-1, and do the reverse for the left check. This is for edge checking.
  6. in this case, you realized after attempting to pass the list[:mid] instead of the start/stop indexes that you would have trouble when figuring out if this was the smallest or largest index of the target. When in doubt, favor the option that gives you more flexibility, hence manage it on your own with start/stop indexes and continue passing the full list in

time: O log(n), binary search
space: O 1, constant space, all work is done in place, no new values stored

class Solution:

  • —def answer(self, arr, target):
  • ——-low = 0
  • ——-high = len(arr)-1
  • ——-# last = self.last_index(arr, low, high, target)
  • ——-first = self.first_index(arr, low, high, target)
  • ——-last = self.last_index(arr, low, high, target)
  • ——-return [first, last]
  • —def first_index(self, arr, low, high, target):
  • ——-if high>=low:
  • ———–mid = low+(high-low)//2
  • ———–if (mid==0 or target > arr[mid-1]) and arr[mid]==target:
  • —————return mid
  • ———–elif target <= arr[mid]:
  • —————return self.first_index(arr, low, mid-1, target)
  • ———–else:
  • —————return self.first_index(arr, mid+1, high, target)
  • ——-return -1
  • —def last_index(self, arr, low, high, target):
  • ——-if high>=low:
  • ———–mid = (high-low)//2 +low
  • ———–if (mid==len(arr)-1 or target=arr[mid]:
  • —————return self.last_index(arr, mid+1, high, target)
  • ———–else:
  • —————return self.last_index(arr, low, mid-1, target)
  • ——-return -1
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

strategies for arrays

A

SORTED use pointers

binary search if you need to look through a sorted array, binary search can return left which is the first index that is larger than it (or it can return len(N), so check for that)

if asked to list combinations of an item, backtracking

There are more array-related data structures or techniques you might want to know. We will not go deeper into most of the concepts in this card but provide the links to the corresponding card in this article.

  1. There are some other data structures which are similar to the array but have some different properties:

String (has been introduced in this card)
Hash Table
Linked List
Queue
Stack
2. As we mentioned, we can call the built-in function to sort an array. But it is useful to understand the principle of some widely-used sorting algorithms and their complexity.

  1. Binary search is also an important technique used to search a specific element in a sorted array.
  2. We have introduced two-pointer technique in this chapter. It is not easy to use this technique flexibly. This technique can also be used to solve:

Slow-pointer and fast-pointer problem in Linked List
Sliding Window Problem
5. The two-pointer technique sometimes will relate to Greedy Algorithm which helps us design our pointers’ movement strategy.

We will come up with more cards to introduce these techniques mentioned above and update the link in the near future.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Product of Array Except Self

Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements of nums except nums[i].

The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer.

You must write an algorithm that runs in O(n) time and without using the division operation.

A

brute force involves a double for loop or using 2 other arrays and doing matrix-type-evaluations on the values before/after the given index.

  1. write out the solution, with arrays and other problems visualization is key and writing it out helps you solve the issue
  2. try a for loop that multiplies each number, then one that skips the first number/last number
  3. loop forward and backward, see what you can find
  4. once you do that, you should see the forward/backward loop contains the final numbers you want if you do some multiplication, you can then figure out how to multiply to get the answer

[1 2 3 4]
forward=[1 1 2 6 ]
backward=[24 12 4 1]

time: O(2n), we loop twice,
space: contant, the response array does not count

class ProductArrayExceptSelf(object):

  • —def answer(self, nums):
  • ——-value=1
  • ——-response=[]
  • ——-for i in range(len(nums)):
  • ———–response.append(value)
  • ———–value*=nums[i]
  • ——-value=1
  • ——-for i in range(len(nums)-1, -1, -1):
  • ———–response[i]*=value
  • ———–value*=nums[i]
  • ——-return response
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Maximum addition subarray

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

A

brute is a double for loop that compares max each iteration

  1. what makes the sum of the entire array less than a subarray? negative numbers
  2. check max on the current index and current index + current value, if the prev current value was negative we would reset it at the current index

time: linear, O(n)
space: constant

class Solution:

  • —def maxSubArray(self, nums: List[int]) -> int:
  • ——-response = current = nums[0]
  • ——-for i in range(1, len(nums)):
  • ———–current = max(nums[i], current + nums[i])
  • ———–response = max(current, response)
  • ——-return response

BONUS: good to know how to solve a problem this way

time: O( N*log(N)), we loop through once then split into smaller arrays and loop through those. classic NlogN
space: log (n), this is based off the recursive calls, since lh and rh are being calculate at different times, we only ever see log(n) calls on the stack

  • —#think merge sort
  • —def merge_sort_esque(self, nums, left, right):
  • ——-if right [greater than]= left:
  • ———–mid = (left+right)//2
  • ———–curr=bl=br=0
  • ———–for i in range(mid+1, right+1):
  • —————curr+=nums[i]
  • —————br=max(br, curr)
  • ———–curr=0
  • ———–for i in range(mid-1, left-1, -1):
  • —————curr+=nums[i]
  • —————bl=max(bl, curr)

————bc = nums[mid]+bl+br

  • ———–lh = self.helper(nums, left, mid-1)
  • ———–rh =self.helper(nums, mid+1, right)
  • ———–return max(bc, lh, rh)
  • ——-else:
  • ———–return -float(‘inf’)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

max product subarray

Given an integer array nums, find a contiguous non-empty subarray within the array that has the largest product, and return the product.

It is guaranteed that the answer will fit in a 32-bit integer.

A subarray is a contiguous subsequence of the array.

A

brute is double for loop with some logic, calculating the max at each iteration. a linear 2 pass solution is to calculate the max going forward/backward.

  • —def brute(self, nums):
  • ——-m=nums[0]
  • ——-for i in range(len(nums)):
  • ———–c=1
  • ———–for j in range(i, len(nums)):
  • —————c*=nums[j]
  • —————m=max(c, m)
  • ——-return m
  1. 2 road blocks, 0’s and negative numbers
  2. 0’s reset the value, but negative numbers do not. if we have an even amount of negatives, then we can multiply everything, but if we have odd, we get a very small number
  3. we want to keep the max, temp_max (p), and minimum (c). this is because at any point, the min value can become the largest value by multiplying it by another negative
  4. the hard part is figuring out how to evaluate the min/temp max at each step, we need to do a triple max/min comparison between the current index, CIp, CIc,
  5. also remember to keep a placeholder temp_max since we use p in both min/max comparisons
  6. right out this solution, writing out solutions helped me get 75% of the way there, but I messed up bc I didn’t check all edge cases

time: O(n), linear, we do 1 pass
space: constant, we only make new variables

class Solution:

  • —def maxProduct(self, nums):
  • ——-_max = temp_max = _min = nums[0]
  • ——-for i in range(1, len(nums)):
  • ———–x = nums[i]
  • ———–temp_max, _min = max(x, x_min, temp_maxx) ,min(x, _minx, temp_maxx)
  • ———–_max = max(_max, temp_max)
  • ——-return _max
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

pascal’s triangle 2

Given an integer rowIndex, return the rowIndexth (0-indexed) row of the Pascal’s triangle.

In Pascal’s triangle, each number is the sum of the two numbers directly above it as shown:

pascals triangle is a triangle where the edges are always 1 and the inner sections are sums of the parts above it

1 
1 1
1 2 1 
1 3 3 1
1 4 6 4 1
A
  1. use dynamic programming to build each row, using that row to build the next one
  2. only keep track of the previous row and the current row
time: O(n^2), we loop through all the row indexes and the lengths of the arrays
space O(n^2 + n), since we make an array at each iteration, the space taken up by all the arrays is n^2, the n includes the answer array. I confirmed recursive calls that create arrays are n * height of tree in space, in this case it is n^2

class Solution:

  • —def getRow(self, rowIndex):
  • ——-return self.helper(rowIndex, [1])
  • —def helper(self, ri, p):
  • ——-if ri==0:
  • ———–return p
  • ——-ri-=1
  • ——-tp=[0]*(len(p)+1)
  • ——-l=len(tp)
  • ——-for i in range(l):
  • ———–if i==0 or i==l-1:
  • —————tp[i]=1
  • ———–else:
  • —————tp[i]=p[i]+p[i-1]
  • ——-return self.helper(ri, tp)

time: O(n^2), we loop through all the row indexes and the lengths of the arrays
space: O(n), linear since we only keep track of the previous array
- —def custom_optimal(self, rowIndex):
- ——-prev, current = deque([1] * (rowIndex + 1)), deque([1] * (rowIndex + 1))
- ——-for i in range(rowIndex + 1):
- ———–for j in range(i):
- —————if j == 0:
- ——————-continue
- —————else:
- ——————-current[j] = prev[j - 1] + prev[j]
- ———–prev, current = current, prev
- ——-return prev

BONUS if you realize you can calculate the next array from the current array, we can even remove the previous array space, making it constant!

  1. calculate the next row from the previous row. 1 3 3 1, makes 4, 6, 4 1, then we add 1 to the beginning (we can do it reversed too)
    - —def optimal(self, rowIndex):
    - ——-p = deque([1])
    - ——-for i in range(rowIndex):
    - ———–for j in range(i):
    - —————p[j] = p[j] + p[j+1]
    - ———–p.appendleft(1)
    - ——-return p
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Given an integer numRows, return the first numRows of Pascal’s triangle.

In Pascal’s triangle, each number is the sum of the two numbers directly above it as shown:

Input: numRows = 5
Output: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

Input: numRows = 1
Output: [[1]]

A
  1. know pascal’s triangle
  2. dynamically build the triangle, start the response with the 0th index of [1] in the response array
  3. create a new array that is equal to i+1, we don’t need to calculate length bc we know i+1=length of the next array
  4. careful with indexing and such

time: O(n^2), we have 5, 4, 3, 2, 1, operations, reduces to n^2
space: the response array is a pascal triangle that takes up n^2 space

class Solution:

  • —def generate(self, numRows: int):
  • ——-response=[[1]]
  • ——-for i in range(1, numRows):
  • ———–li=[1]*(i+1)
  • ———–for j in range(i+1):
  • —————if j==0 or j==i:
  • ——————-li[j]=1
  • —————else:
  • ——————-li[j]=response[i-1][j]+response[i-1][j-1]
  • ———–response.append(li)
  • ——-return response
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Kth Largest Element in an Array

Given an integer array nums and an integer k, return the kth largest element in the array.

Note that it is the kth largest element in the sorted order, not the kth distinct element.

A

brute force is to do quick sort/heap sort/any sort, then do len(nums)-k and return that index (7-1 gives 6, which would be the largest in an ascending sorted array)

  1. quick select
  2. quicksort but instead of sorting both splits just sort the one where the target>partition or target int
  3. we also do not have to worry about if high >= low
    BONUS: check if high == low, if that is the case array has one element or we have quick selected to the last available element, so it is the answer

time: O(n) average, worst case same as quick sort which is N^2
space: O(log(n)) average, worst case would be O(n). This comes from the recursive calls resulting from a bad pivot

class Solution:

  • —def findKthLargest(self, nums, k):
  • ——-n = len(nums)
  • ——-high = n - 1
  • ——-low = 0
  • ——-j_smallest = n - k
  • ——-return self.quickselect(nums, high, low, j_smallest)
  • —def quickselect(self, arr, high, low, j):
  • ——-if high == low:
  • ———–return arr[low]
  • ——-pi = self.partition(arr, high, low)
  • ——-if pi == j:
  • ———–return arr[j]
  • ——-elif pi [less than] j:
  • ———–return self.quickselect(arr, high, pi + 1, j)
  • ——-else:
  • ———–return self.quickselect(arr, pi - 1, low, j)
  • —def partition(self, arr, high, low):
  • ——-pivot = random.randint(low, high)
  • ——-arr[pivot], arr[high] = arr[high], arr[pivot]
  • ——-swap = low
  • ——-for i in range(low, high):
  • ———–if arr[i] [less than] arr[high]:
  • —————arr[swap], arr[i] = arr[i], arr[swap]
  • —————swap += 1
  • ——-arr[swap], arr[high] = arr[high], arr[swap]
  • ——-return swap
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Combinations

Given two integers n and k, return all possible combinations of k numbers out of the range [1, n].

You may return the answer in any order.

A

easiest way is to use itertools.combinations(iterable, length). get the iterable from range(1, n+1), then loop over the result and append them to the list. (make sure to convert the tuples to lists first)

  1. backtracking!
  2. backtracking requires that we are able to undo operations further down the tree
  3. figure out a way to remove items from a list when you are done with it, pop() is the choice
  4. when do you pop? after return or after function call? it has to be after the function call, bc if not you will see that it never fully “resets” the temp list. with backtracking, we need to always be able to reset the algo as we go through it
  5. when appending, make sure you append a deep copy, which is list[:], or else you only pass a reference and will continue to operate on that list within the output

time: O(k*combination count) N!/(k!(n-k)!) is the formula for the number of combinations given options N and length K. appending a list takes K time where k is the length of the list, which is why is has the k
space: O(combination count) which is the space of the final return list. if not counting that, it would be N which is the heigh of the recursion tree

class Solution:

  • —def combine(self, n: int, k: int):——–
  • ——-self.output=[]
  • ——-self.helper(n, k, 1, [])—————-
  • ——-return self.output
  • —def helper(self, n, k, index, temp):
  • ——-if len(temp)==k:
  • ———–self.output.append(temp[:])
  • ——-else:
  • ———–for j in range(index, n+1):
  • —————temp.append(j)
  • —————self.helper(n, k, j+1, temp)
  • —————temp.pop()
  • —def using_modules(self, n, k):
  • ——-import itertools
  • ——-output=[]
  • ——-nums=range(1,n+1)
  • ——-combos = itertools.combinations(nums, k)
  • ——-for c in combos:
  • ———–output.append(list(c))
  • ——-return output
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

permutations

Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.

A

brute is to use itertools.permutations(iterable, length), returns a list of tuples. conver the tuples to lists and append to output

  1. backtracking!
  2. create a set for seen numbers, if seen, skip over them in the loop
  3. base case is if len(current)==output, then we append that to the output!

time: N!, since it takes N! / (N-K)! to calculate the permutations. N is the values to choose from, K is the size (9 digit keypad, 4 digit code)
space: N!, since that is the output array, recursion depth is N

class Solution:

  • —def permute(self, nums: List[int]) -> List[List[int]]:
  • ——-output=[]
  • ——-self.backtracking(nums, set(), [], output)
  • ——-return output
  • —def backtracking(self, nums, seen, current, output):
  • ——-if len(current)==len(nums):
  • ———–output.append(current[:])
  • ——-else:
  • ———–for x in nums:
  • —————if x not in seen:
  • ——————-current.append(x)
  • ——————-seen.add(x)
  • ——————-self.backtracking(nums, seen, current, output)
  • ——————-current.pop()
  • ——————-seen.remove(x)
  • —def permute(self, nums: List[int]) -> List[List[int]]:
  • ——-import itertools
  • ——-output=[]
  • ——-permuations = itertools.permutations(nums, len(nums))
  • ——-for x in permuations:
  • ———–output.append(list(x))
  • ——-return output
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Find The Duplicates

Given two sorted arrays arr1 and arr2 of passport numbers, implement a function findDuplicates that returns an array of all passport numbers that are both in arr1 and arr2. Note that the output array should be sorted in an ascending order.

A
  1. equal sizes, think of pointers
  2. while loop making sure neither pointer is equal to length of respective array

time: O(n+m) since worst case we traverse through both of the arrays fully,
space: is output (constant)

class Solution:

  • —def equal_sizes(self, arr1, arr2):
  • ——-output=[]
  • ——-p1,p2 = 0,0
  • ——-while p1[less than]len(arr1) and p2[less than]len(arr2):
  • ———–if arr1[p1] is arr2[p2]:
  • —————output.append(arr1[p1])
  • —————p1+=1
  • —————p2+=1
  • ———–elif arr1[p1] [less than] arr2[p2]:
  • —————p1+=1
  • ———–else:
  • —————p2+=1
  • ——-return output
  1. duplicates must be in both arrays, so we do a complete loop through the small array and a binary search through the larger array

time: N log(m) where n is smaller and M is larger
space: output array (constant)

  • —def not_equal_sizes(self, arr1, arr2):
  • ——-if len(arr1)[less than]len(arr2):
  • ———–small=arr1
  • ———–larger=arr2
  • ——-else:
  • ———–large=arr1
  • ———–small=arr2
  • ——-output=[]
  • ——-for x in small:
  • ———–if self.binary_search(x, large):
  • —————output.append(x)
  • ——-return output
  • —def binary_search(self, target, arr):
  • ——-low=0
  • ——-high=len(arr)-1
  • ——-while high[greater than]=low:
  • ———–mid = (high+low)//2
  • ———–if arr[mid] is target:
  • —————return True
  • ———–elif target [less than] arr[mid]:
  • —————high=mid-1
  • ———–else:
  • —————low=mid+1
  • ——-return False
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Largest rectangle in histograms

Given an array of integers heights representing the histogram’s bar height where the width of each bar is 1, return the area of the largest rectangle in the histogram.

A

brute force would be to do a double for loop, calculating the area based on the minimum value seen since that is the limiting factor of the rectangle area

class Solution:

  • —def largestRectangleArea(self, heights):
  • ——-output = 0
  • ——-for i in range(len(heights)):
  • ———–minimum=heights[i]
  • ———–for j in range(i, len(heights)):
  • —————current = heights[j]
  • —————minimum = min(minimum, current)
  • —————response = minimum*(j-i+1)
  • —————output = max(output, response, current)
  • ——-return output
  1. find the min in the array, min*length = max area for now, then remove the min and divide and conquer!
  2. Once you get that min, go left and go right, removing that min index, similar to binary search

time: N log N
space: constant
- —def divide_conquer(self, heights):
- ——-output = 0
- ——-for i in range(len(heights)):
- ———–minimum=heights[i]
- ———–for j in range(i, len(heights)):
- —————current = heights[j]
- —————minimum = min(minimum, current)
- —————response = minimum*(j-i+1)
- —————output = max(output, response, current)
- ——-return output

  • —def optimal(self, heights):
  • ——-stack = [-1]
  • ——-max_area = 0
  • ——-for i in range(len(heights)):
  • ———–while stack[-1] != -1 and heights[stack[-1]] [greater than]= heights[i]:
  • —————current_height = heights[stack.pop()]
  • —————current_width = i - stack[-1] - 1
  • —————max_area = max(max_area, current_height * current_width)
  • ———–stack.append(i)
  • ——-while stack[-1] != -1:
  • ———–current_height = heights[stack.pop()]
  • ———–current_width = len(heights) - stack[-1] - 1
  • ———–max_area = max(max_area, current_height * current_width)
  • ——-return max_area
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Min Stack

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

Implement the MinStack class:

MinStack() initializes the stack object.
void push(int val) pushes the element val onto the stack.
void pop() removes the element on the top of the stack.
int top() gets the top element of the stack.
int getMin() retrieves the minimum element in the stack.
A
  1. make sure the append operations keep track of previous min stack values
  2. make sure that when you pop off values, if that value is the min stack value, you have to account for that by getting a previous stack value
  3. make sure that you account for numbers of equal value being added to the stack, this is important for the min stack values

time: everything is linear
space: the stack itself is N, the min_stack could be N space too worst case

class MinStack:

  • —def __init__(self):
  • ——-self.stack = []
  • ——-self.min_stack = []
  • —def push(self, val: int) -[greater than] None:
  • ——-if not self.min_stack or val [less than]= self.min_stack[-1]:
  • ———–self.min_stack.append(val)
  • ——-self.stack.append(val)
  • —def pop(self) -[greater than] None:
  • ——-val = self.stack.pop()
  • ——-if val == self.min_stack[-1]:
  • ———–self.min_stack.pop()
  • —def top(self) -[greater than] int:
  • ——-if len(self.stack) [greater than] 0:
  • ——-else:
  • ———–return None
  • —def getMin(self) -[greater than] int:
  • ——-if len(self.min_stack) [greater than] 0:
  • ———–return self.min_stack[-1]
  • ——-else:
  • ———–return None
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

find pivot index

Given an array of integers nums, calculate the pivot index of this array.

The pivot index is the index where the sum of all the numbers strictly to the left of the index is equal to the sum of all the numbers strictly to the index’s right.

If the index is on the left edge of the array, then the left sum is 0 because there are no elements to the left. This also applies to the right edge of the array.

Return the leftmost pivot index. If no such index exists, return -1.

Example 1:

Input: nums = [1,7,3,6,5,6]
Output: 3
Explanation:
The pivot index is 3.
Left sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11
Right sum = nums[4] + nums[5] = 5 + 6 = 11

A

brute force could be a 2 pointer, calculate sum on each side of inner-loop, return if true

I tried a 2-pointer solution, but there are too many edge cases with negative numbers that make a left/right sum with pointers extremely difficult

  1. calculate sum of array
  2. iterate through array and add values to left. When we find sum - left - current == left, we found the pivot!

time: O(N)
space: constant

class Solution:

  • —def pivotIndex(self, nums):
  • ——-if len(nums) == 0:
  • ———–return -1
  • ——-total = sum(nums)
  • ——-left = 0
  • ——-for i in range(len(nums)):
  • ———–if left == (total - left - nums[i]):
  • —————return i
  • ———–else:
  • —————left += nums[i]
  • ——-return -1
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

largest number at least twice of others

You are given an integer array nums where the largest integer is unique.

Determine whether the largest element in the array is at least twice as much as every other number in the array. If it is, return the index of the largest element, or return -1 otherwise.

A

keep track of the largest and second-largest index, at the end check if the largest index is greater than or equal twice that of the second-largest

time: O(N):
space: constant

class Solution:

  • —def dominantIndex(self, nums):
  • ——-if len(nums) == 0:
  • ———–return -1
  • ——-if len(nums) == 1:
  • ———–return 0
  • ——-if nums[0] > nums[1]:
  • ———–largest_index = 0
  • ———–second_index = 1
  • ——-else:
  • ———–largest_index = 1
  • ——-for i in range(2, len(nums)):
  • ———–if nums[i] > nums[largest_index]:
  • —————largest_index, second_index = i, largest_index
  • ———–elif nums[i] > nums[second_index]:
  • —————second_index = i
  • ——-return largest_index if nums[largest_index] >= nums[second_index] * 2 else -1
21
Q

plus one

You are given a large integer represented as an integer array digits, where each digits[i] is the ith digit of the integer. The digits are ordered from most significant to least significant in left-to-right order. The large integer does not contain any leading 0’s.

Increment the large integer by one and return the resulting array of digits.

A
  1. very similar to linked list addition but way easier, remember you have to a carry value if the LSD (least significant digit) is 9

time: O(N)
space: O(N) in worst case bc if we have they’re all 9, we need to add a 1 to the beginning which will require N space

class Solution:

  • —def custom(self, digits):
  • ——-index = len(digits) - 1
  • ——-div, mod = divmod(digits[index] + 1, 10)
  • ——-digits[index] = mod
  • ——-index -= 1
  • ——-while div > 0 and index >=0:
  • ———–div, mod = divmod(digits[index] + div, 10)
  • ———–digits[index] = mod
  • ———–index -= 1
  • ——-if div > 0:
  • ———–digits.insert(0, 1)
  • ——-return digits
  • —def plusOne(self, digits):
  • ——-n = len(digits)
  • ——-for i in range(n):
  • ———–current = n - 1 - i
  • ———–if digits[current] == 9:
  • —————digits[current] = 0
  • ———–else:
  • —————digits[current] += 1
  • —————return digits
  • ——-return [1] + digits
22
Q

top k frequent words

Given an array of strings words and an integer k, return the k most frequent strings.

Return the answer sorted by the frequency from highest to lowest. Sort the words with the same frequency by their lexicographical order.

A
  1. top K anything, probably a heap!
  2. you need a custom comparator, compares regularly for the counter, then reverse for the letter
  3. nlargest and nsmallest use the __lt__ operator in python, we know that nlargest for [a,b,c] would give a reverse order, so we need to reverse the order the __lt__ operator would give when we compare based on letters (since we want the smallest comparing the letters, largest comparing ints)
  4. make sure to adhere to the output requirements, make the __str__ method return self.letter

time: O(n * log(k)), standard heapq.nlargest time complexity. linear time to make the counter, linear time to make the initial heap
space: linear spae for the counter/heap, k space for the heap

TIME2: O(Klogn)), this is achieved by max-heapifying the array (O(n) time to heapify), then performing max_heappop operations for the range of k. heappop is log(N) and we do it K times, so K*log(n)
SPACE2: linear,

import heapq
from collections import defaultdict, Counter
class Solution:
—-def kLogN(self, words, k):
——–frequency = Counter(words)
——–heap = []
——–for word, freq in frequency.items():
————heap.append(Comparator(freq, word))
——–
——–heapq._heapify_max(heap)
——–return [str(heapq._heappop_max(heap)) for _ in range(k)]

—-def NlogK(self, words, k):
——–frequency = Counter(words)
——–# frequency = defaultdict(int)
——–# for word in words:
————# frequency[word] += 1
——–heap = []
——–for word, freq in frequency.items():
————heap.append(Comparator(freq, word))
——–
——–response = heapq.nlargest(k, heap)
——–return [str(x) for x in response]
————
————
class Comparator:
—-def __init__(self, count, letter):
——–self.count = count
——–self.letter = letter
——–
—-def __lt__(self, other):
——–if self.count == other.count:
————return self.letter > other.letter
——–return self.count < other.count
—-
—-def __str__(self):
——–return str(self.letter)

print(Solution().kLogN([“i”,”love”,”leetcode”,”i”,”love”,”coding”], 2))

23
Q

pascals triangle

Given an integer numRows, return the first numRows of Pascal’s triangle.

In Pascal’s triangle, each number is the sum of the two numbers directly above it as shown:

Example 1:

Input: numRows = 5
Output: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

A
  1. must know pascal’s triangle
  2. if j == 0 or j == i, then we are at the edge of the triangle, so we make it 1, otherwise use the previous row in the response array
  3. you can also make a prev array and keep track of it that way

time: O(n^2), we do a double loop from 0 to N, which is by definition quadradic
space: O(n^2) if we count the output array, else it is constant

class Solution:

  • —def generate(self, numRows):
  • ——-response = []
  • ——-for i in range(numRows):
  • ———–row = []
  • ———–for j in range(i + 1):
  • —————if j == 0 or j == i:
  • ——————-row.append(1)
  • —————else:
  • ——————-num = response[i - 1][j] + response[i - 1][j - 1]
  • ——————-row.append(num)
  • ———–response.append(row)
  • ——-return response
24
Q

Remove element from array

Given an integer array nums and an integer val, remove all occurrences of val in nums in-place. The relative order of the elements may be changed.

Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements.

Return k after placing the final result in the first k slots of nums.

Do not allocate extra space for another array. You must do this by modifying the input array in-place with O(1) extra memory.

A
  1. left/right pointers, when we find the bad value, set our value to the rightmost value, then pop off the last value. This helps us deal with actually deleting items from the array!

time: O(N), we do 1 loop from both ends
space: constant

class Solution:

  • —def actually_remove(self, nums, val):
  • ——-left = 0
  • ——-while left <= len(nums) - 1:
  • ———–if nums[left] == val:
  • —————nums[left] = nums[len(nums) - 1]
  • —————nums.pop()
  • ———–else:
  • —————left += 1
  • ——-return left
  1. move elements to back, mark the length of the array without elements in back

time: O(n), we have to iterate through the array
space: constant

  • —def move_to_back(self, nums, val):
  • ——-k = 0
  • ——-for i in range(len(nums)):
  • ———–if nums[i] != val:
  • —————nums[k] = nums[i]
  • —————k += 1
  • ——-return k
25
Q

max consecutive ones

Given a binary array nums, return the maximum number of consecutive 1’s in the array.

Example 1:

Input: nums = [1,1,0,1,1,1]
Output: 3
Explanation: The first two digits or the last three digits are consecutive 1s. The maximum number of consecutive 1s is 3.

A

I only included this bc the for loop does not work, what if the last 3 items are the max? we never get to check bc of the for loop,
so after the loop we have to check again 1 final time

time: O(n)
space: constant

class Solution:

  • —def findMaxConsecutiveOnes(self, nums):
  • ——-response, current = 0, 0
  • ——-for x in nums:
  • ———–if x == 1:
  • —————current += 1
  • ———–else:
  • —————response = max(response, current)
  • —————current = 0
  • ——-return max(response, current)
26
Q

minimum size subarray

Given an array of positive integers nums and a positive integer target, return the minimal length of a contiguous subarray [numsl, numsl+1, …, numsr-1, numsr] of which the sum is greater than or equal to target. If there is no such subarray, return 0 instead.

Example 1:

Input: target = 7, nums = [2,3,1,2,4,3]
Output: 2
Explanation: The subarray [4,3] has the minimal length under the problem constraint.

A

brute force is double for loop, you can also use binary search? what?

  • —def minSubArrayLen(self, target, nums):
  • ——-if not nums:
  • ———–return 0
  • ——-if len(nums) == 1:
  • ———–if nums[0] == target:
  • —————return 1
  • ———–else:
  • —————return 0
  • ——-left, right = 0, 1
  • ——-response = float(“inf”)
  • ——-current = nums[0]
  • ——-while left < len(nums):
  • ———–if current >= target:
  • —————response = min(response, right - left)
  • —————current -= nums[left]
  • —————left += 1
  • ———–elif current < target and right < len(nums):
  • —————current += nums[right]
  • —————right += 1
  • ———–else: #it is less than, but right is at the end
  • —————left += 1
  • ——-return response if response != float(“inf”) else 0
  • —def other(self, target, nums):
  • ——-_sum = sum(nums)
  • ——-if _sum < target:
  • ———–return 0
  • ——-if _sum == target:
  • ———–return len(nums)
  • ——-l, r = 0, -1
  • ——-length = len(nums)
  • ——-_sum = 0
  • ——-while l < len(nums):
  • ———–if _sum < target and r + 1 < len(nums):
  • —————r += 1
  • —————_sum += nums[r]
  • ———–else:
  • —————_sum -= nums [l]
  • —————l += 1
  • ———–if _sum >= target:
  • —————length = min(r - l + 1, length)
  • ——-return length
27
Q

rotate array

Given an array, rotate the array to the right by k steps, where k is non-negative.

Example 1:

Input: nums = [1,2,3,4,5,6,7], k = 3
Output: [5,6,7,1,2,3,4]
Explanation:
rotate 1 steps to the right: [7,1,2,3,4,5,6]
rotate 2 steps to the right: [6,7,1,2,3,4,5]
rotate 3 steps to the right: [5,6,7,1,2,3,4]

A
from collections import deque
class Solution:

time: O(N), 2N bc we reverse the nums, then we reverse it again in parts
space: constant

  • —def reverse_parts(self, nums, k):
  • ——-k = k % len(nums)
  • ——-if k != 0:
  • ———–self.reverse(0, len(nums) - 1, nums)
  • ———–self.reverse(0, k - 1, nums)
  • ———–self.reverse(k, len(nums) - 1, nums)
  • —def reverse(self, left, right, nums):
  • ——-while left < right:
  • ———–nums[left], nums[right] = nums[right], nums[left]
  • ———–left += 1
  • ———–right -= 1

time: O(N), 3n bc we create a new array, loop through the range, then copy it into the original array
space: O(N), N for new array

  • —def brute_with_optimization(self, nums, k):
  • ——-true_rotations = k % len(nums)
  • ——-final = [0] * len(nums)
  • ——-for i in range(len(nums)):
  • ———–final[(i + true_rotations) % len(nums)] = nums[i]
  • ——-nums[:] = final
28
Q

remove duplicates in sorted array

Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique element appears only once. The relative order of the elements should be kept the same.

Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements.

Return k after placing the final result in the first k slots of nums.

Do not allocate extra space for another array. You must do this by modifying the input array in-place with O(1) extra memory.

Custom Judge:

The judge will test your solution with the following code:

int[] nums = […]; // Input array
int[] expectedNums = […]; // The expected answer with correct length

int k = removeDuplicates(nums); // Calls your implementation

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}

A

time: linear
space: constant

class Solution:

  • —def removeDuplicates(self, nums):
  • ——-if not nums:
  • ———–return 0
  • ——-cur = nums[0]
  • ——-count = 1
  • ——-for i in range(1, len(nums)):
  • ———–if nums[i] != cur:
  • —————cur = nums[i]
  • —————nums[count] = nums[i]
  • —————count += 1
  • ——-return count
28
Q

remove duplicates in sorted array

Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique element appears only once. The relative order of the elements should be kept the same.

Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements.

Return k after placing the final result in the first k slots of nums.

Do not allocate extra space for another array. You must do this by modifying the input array in-place with O(1) extra memory.

Custom Judge:

The judge will test your solution with the following code:

int[] nums = […]; // Input array
int[] expectedNums = […]; // The expected answer with correct length

int k = removeDuplicates(nums); // Calls your implementation

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}

A

time: linear
space: constant

class Solution:

  • —def removeDuplicates(self, nums):
  • ——-if not nums:
  • ———–return 0
  • ——-cur = nums[0]
  • ——-count = 1
  • ——-for i in range(1, len(nums)):
  • ———–if nums[i] != cur:
  • —————cur = nums[i]
  • —————nums[count] = nums[i]
  • —————count += 1
  • ——-return count
29
Q

move zeros

Given an integer array nums, move all 0’s to the end of it while maintaining the relative order of the non-zero elements.

Note that you must do this in-place without making a copy of the array.

Example 1:

Input: nums = [0,1,0,3,12]
Output: [1,3,12,0,0]

A

brute is when you iterate, make a swap index array with all the indexes and where you would swap them, then iterate through the array and swap those indexes. linear time and space

  1. think quick sort, we have a low index (start) and we can do a 1 pass iteration that swaps values in the array effectively
    time: linear, 1 pass
    space: constant

class Solution(object):

  • —def answer(self, nums):
  • ——-start = 0
  • ——-for i, x in enumerate(nums):
  • ———–if x != 0:
  • —————nums[start], nums[i] = nums[i], nums[start]
  • —————start += 1
  • ——-return nums

time: linear, 1.5 passes
space: constant

  • —def answer2(self, nums):
  • ——-start=0
  • ——-for i, x in enumerate(nums):
  • ———–if x!=0:
  • —————nums[start] = nums[i]
  • —————start += 1
  • ——-for j in range(start, len(nums)):
  • ———–nums[j]=0
30
Q

You are given a character array containing a set of words separated by whitespace.

Your task is to modify that character array so that the words all appear in reverse order.

Example

input - [‘A’, ‘l’, ‘i’, ‘c’, ‘e’, ‘ ‘, ‘l’, ‘i’, ‘k’, ‘e’, ‘s’, ‘ ‘, ‘B’, ‘o’, ‘b’]

output - [‘B’, ‘o’, ‘b’, ‘ ‘, ‘l’, ‘i’, ‘k’, ‘e’, ‘s’, ‘ ‘, ‘A’, ‘l’, ‘i’, ‘c’, ‘e’]

A

reverse the list, then at each space call a reverse function on the slice of the list, reverse(start, i) where start begins at 0, then increments to i+1, then after the loop we have to remember to reverse the last section bc the for-loop doesn’t catch the last word

time: O(n), 2N technically bc we loop twice
space: constant

class Solution:

  • —def answer(self, input):
  • ——-self.remove_trailing_leading_spaces(input)
  • ——-self.remove_inside_spaces(input)
  • ——-return self.reverse_words(input)
  • —def remove_inside_spaces(self, input):
  • ——-first_white_space = False
  • ——-for i in range(len(input) -1, -1, -1):
  • ———–if input[i] == “ “:
  • —————if first_white_space:
  • ——————-del input[i]
  • —————else:
  • ——————-first_white_space = True
  • ———–else:
  • —————first_white_space = False
  • —def remove_trailing_leading_spaces(self, input):
  • ——-while input[-1] == “ “:
  • ———–input.pop()
  • ——-input.reverse()
  • ——-while input[-1] == “ “:
  • ———–input.pop()
  • ——-input.reverse()
  • —def reverse_words(self, input):
  • ——-input = input[::-1]
  • ——-start = 0
  • ——-in_word = False
  • ——-for i in range(len(input)):
  • ———–if input[i] != “ “:
  • —————if not in_word:
  • ——————-start = i
  • ——————-in_word = True
  • ———–elif in_word:
  • —————self.reverse(start, i, input)
  • —————in_word = False
  • ——-if in_word:
  • ———–self.reverse(start, len(input), input)
  • ——-return input
  • —def reverse(self, start, stop, input):
  • ——-stop -= 1
  • ——-while stop > start:
  • ———–input[start], input[stop] = input[stop], input[start]
  • ———–start += 1
  • ———–stop -= 1
31
Q

reverse words 4

You are given a character array containing a set of words separated by whitespace.

Your task is to modify that character array so that the words all appear in reverse order. There can be trailing/leading and extra spaces in the arr!

Do this without using any extra space.

Example

input - [‘A’, ‘l’, ‘i’, ‘c’, ‘e’, ‘ ‘, ‘l’, ‘i’, ‘k’, ‘e’, ‘s’, ‘ ‘, ‘B’, ‘o’, ‘b’]

output - [‘B’, ‘o’, ‘b’, ‘ ‘, ‘l’, ‘i’, ‘k’, ‘e’, ‘s’, ‘ ‘, ‘A’, ‘l’, ‘i’, ‘c’, ‘e’]

A
  1. trick is dealing with the extra white space
  2. pop white spaces off end, reverse it, do it again, then reverse it back to normal
  3. you can delete items in an array safely if you iterate backward. Deleting index 5 in an array of length 6 is okay bc the next index is 4, so you wont get an out of index error

time: O(n), we reverse the list twice, iterate to delete trailing white spaces, then iterate over the list and reverse the words in the list. O(4N + M) where M is the time to reverse just the words in the list
space: constant!

The more time is a huge tradeoff, you could just do it in N + M time where M is the length of the words and single white spaces by adding a response array, then appending the reversed words to that array instead of doing it in place. You will need to keep track of adding white spaces after each word and such, that is tricky

class Solution:

  • —def answer(self, input):
  • ——-self.remove_trailing_leading_spaces(input)
  • ——-self.remove_inside_spaces(input)
  • ——-return self.reverse_words(input)
  • —def remove_inside_spaces(self, input):
  • ——-first_white_space = False
  • ——-for i in range(len(input) -1, -1, -1):
  • ———–if input[i] == “ “:
  • —————if first_white_space:
  • ——————-del input[i]
  • —————else:
  • ——————-first_white_space = True
  • ———–else:
  • —————first_white_space = False
  • —def remove_trailing_leading_spaces(self, input):
  • ——-while input[-1] == “ “:
  • ———–input.pop()
  • ——-input.reverse()
  • ——-while input[-1] == “ “:
  • ———–input.pop()
  • ——-input.reverse()
  • —def reverse_words(self, input):
  • ——-input = input[::-1]
  • ——-start = 0
  • ——-in_word = False
  • ——-for i in range(len(input)):
  • ———–if input[i] != “ “:
  • —————if not in_word:
  • ——————-start = i
  • ——————-in_word = True
  • ———–elif in_word:
  • —————self.reverse(start, i, input)
  • —————in_word = False
  • ——-if in_word:
  • ———–self.reverse(start, len(input), input)
  • ——-return input
  • —def reverse(self, start, stop, input):
  • ——-stop -= 1
  • ——-while stop > start:
  • ———–input[start], input[stop] = input[stop], input[start]
  • ———–start += 1
  • ———–stop -= 1
32
Q

first bad version

You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.

Suppose you have n versions [1, 2, …, n] and you want to find out the first bad one, which causes all the following ones to be bad.

You are given an API bool isBadVersion(version) which returns whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.

Example 1:

Input: n = 5, bad = 4
Output: 4
Explanation:
call isBadVersion(3) -> false
call isBadVersion(5) -> true
call isBadVersion(4) -> true
Then 4 is the first bad version.
A
  1. my custom solution works, we basically check if the next one up or down is the opposite, bc then we have found the first bad version!
  2. for the custom, it is simple binary search, much like quick sort, you need to increment the partition one way or the other so you don’t redo an index! to see why you return low, do a test case where we have …. good, bad….. and we get the last good index as low, or the first bad index as high. when we go through the binary search the last bad index as good makes mid the last good index, which will eventually return the first bad index. The vise-versa is true if we do the reverse

time: Log(n)
space: constant

class Solution:

  • —def firstBadVersion(self, n):
  • ——-def isBadVersion(n):
  • ———–pass
  • ——-low = 0
  • ——-high = n
  • ——-while high >= low:
  • ———–mid = (low + high) // 2
  • ———–if isBadVersion(mid):
  • —————if mid != 0:
  • ——————-if not isBadVersion(mid - 1):
  • ———————–return mid
  • —————else:
  • ——————-return mid
  • —————high = mid
  • ———–else:
  • —————if mid != n:
  • ——————-if isBadVersion(mid + 1):
  • ———————–return mid + 1
  • —————else:
  • ——————-if isBadVersion(mid):
  • ———————–return mid
  • —————low = mid
  • —def optimized_binary_search(self, n):
  • ——-def isBadVersion(n):
  • ———–pass
  • ——-low = 0
  • ——-high = n
  • ——-while high >= low:
  • ———–mid = (low + high) // 2
  • ———–if isBadVersion(mid):
  • —————high = mid - 1
  • ———–else:
  • —————low = mid + 1
  • ——-return low
33
Q

Intersection of Two Arrays (common elements between 2 arrays, intersection is confusing)

Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must be unique and you may return the result in any order.

Example 1:

Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2]

A

time: O(nums1 + nums2), we convert them to sets which takes N time for each, then we find the intersection which takes min(n, m) time
space: O(nums1 + nums2), the space for sets and response

class Solution:

  • —# interesction means common elements between the 2
  • —def intersection(self, nums1, nums2):
  • ——-set1 = set(nums1)
  • ——-set2 = set(nums2)
  • ——-if len(set1) < len(set2):
  • ———–return [x for x in set1 if x in set2]
  • ——-else:
  • ———–return [x for x in set2 if x in set1]
  • —def intersection(self, nums1, nums2):
  • ——-set1 = set(nums1)
  • ——-set2 = set(nums2)
  • ——-return set1.intersection(set2)
34
Q

find numbers with even number of digits

Given an array nums of integers, return how many of them contain an even number of digits.

Example 1:

Input: nums = [12,345,2,6,7896]
Output: 2
Explanation:
12 contains 2 digits (even number of digits).
345 contains 3 digits (odd number of digits).
2 contains 1 digit (odd number of digits).
6 contains 1 digit (odd number of digits).
7896 contains 4 digits (even number of digits).
Therefore only 12 and 7896 contain an even number of digits.

A

time: O(n * log(n)), we go through each element and count their digits, counting digits takes log time
space: constant

class Solution:

  • —def findNumbers(self, nums):
  • ——-response = 0
  • ——-for x in nums:
  • ———–count = 0
  • ———–while x > 0:
  • —————x //= 10
  • —————count += 1
  • ———–if count % 2 == 0:
  • —————response += 1
  • ——-return response
35
Q
  1. Merge Sorted Array

You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, representing the number of elements in nums1 and nums2 respectively.

Merge nums1 and nums2 into a single array sorted in non-decreasing order.

The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, and the last n elements are set to 0 and should be ignored. nums2 has a length of n.

Example 1:

Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
Output: [1,2,2,3,5,6]
Explanation: The arrays we are merging are [1,2,3] and [2,5,6].
The result of the merge is [1,2,2,3,5,6] with the underlined elements coming from nums1.

A
  1. think merge sort!

time: O(n + m), we go through all items in both lists
space: O(m ) bc we need to make a copy of nums1

class Solution:

  • —def merge(self, nums1, m, nums2, n):
  • ——-nums1_copy = nums1[:m + 1]
  • ——-i = j = k = 0
  • ——-while i < m and j < n:
  • ———–if nums1_copy[i] < nums2[j]:
  • —————nums1[k] = nums1_copy[i]
  • —————i += 1
  • ———–else:
  • —————nums1[k] = nums2[j]
  • —————j += 1
  • ———–k += 1
  • ——-while i < m:
  • ———–nums1[k] = nums1_copy[i]
  • ———–k += 1
  • ———–i += 1
  • ——-while j < n:
  • ———–nums1[k] = nums2[j]
  • ———–j += 1
  • ———–k += 1
36
Q

Check If N and Its Double Exist

Given an array arr of integers, check if there exists two integers N and M such that N is the double of M ( i.e. N = 2 * M).

More formally check if there exists two indices i and j such that :

i != j
0 <= i, j < arr.length
arr[i] == 2 * arr[j]

Example 1:

Input: arr = [10,2,5,3]
Output: true
Explanation: N = 10 is the double of M = 5,that is, 10 = 2 * 5.

A
  1. do it in 1 loop if you realize you can check if the double or the half exists. if the half exists (NOT FLOOR DIVISION, 3 AND 7 DO NOT WORK)

time: O(n)
space: o(n)

class Solution:

  • —def checkIfExist(self, arr):
  • ——-doubles_set = set()
  • ——-for i in range(len(arr)):
  • ———–double_key = 2 * arr[i]
  • ———–half_key = arr[i] / 2
  • ———–if (double_key in doubles_set) or (half_key in doubles_set):
  • —————return True
  • ———–doubles_set.add(arr[i])

——–return False

37
Q
  1. Valid Mountain Array

Given an array of integers arr, return true if and only if it is a valid mountain array.

Recall that arr is a mountain array if and only if:

arr.length >= 3
There exists some i with 0 < i < arr.length - 1 such that:
arr[0] < arr[1] < … < arr[i - 1] < arr[i]
arr[i] > arr[i + 1] > … > arr[arr.length - 1]

Example 1:

Input: arr = [2,1]
Output: false

A

while loop solution is tricky, I think my custom one makes most intuitive sense to me

  1. needs to have gone up, and gone down. if it has gone up and transitioned to down it cannot go down again. Use booleans to make this reality
  2. return down bc if up = true then down = true, down will be the boolean we can return. If it only goes up down will be false!

time: O(n)
space: O(1)

class Solution:

  • —def validMountainArray(self, arr):
  • ——-if len(arr) < 3:
  • ———–return False
  • ——-up = False
  • ——-down = False
  • ——-prev = arr[0]
  • ——-for x in arr[1:]:
  • ———–if x > prev and not down:
  • —————up = True
  • ———–elif x < prev and up:
  • —————down = True
  • ———–else:
  • —————return False
  • ———–prev = x
  • ——-return down
  • —def while_loop(self, arr):
  • ——-if len(arr) < 3:
  • ———–return False
  • ——-start = 0
  • ——-while start < end - 1 and arr[start] < arr[start + 1]:
  • ———–start += 1
  • ——-if start == 0 or start == end - 1:
  • ———–return False
  • ——-while start < end -1 and arr[start] > arr[start + 1]:
  • ———–start += 1
  • ——-return start == end - 1
38
Q
  1. Replace Elements with Greatest Element on Right Side

Given an array arr, replace every element in that array with the greatest element among the elements to its right, and replace the last element with -1.

After doing so, return the array.

Example 1:

Input: arr = [17,18,5,4,6,1]
Output: [18,6,6,6,1,-1]
Explanation:
- index 0 –> the greatest element to the right of index 0 is index 1 (18).
- index 1 –> the greatest element to the right of index 1 is index 4 (6).
- index 2 –> the greatest element to the right of index 2 is index 4 (6).
- index 3 –> the greatest element to the right of index 3 is index 4 (6).
- index 4 –> the greatest element to the right of index 4 is index 5 (1).
- index 5 –> there are no elements to the right of index 5, so we put -1.

A
  1. go backward! use max, replace it in-place

time: O(n)
space: o(1)

class Solution:
----def replaceElements(self, arr):
--------_max = -1
--------for i in range(len(arr) - 1, -1, -1):
#Note that if the values are very small negatives, they would be replaced with -1, is that what we want? unsure
------------arr[i], _max = _max, max(_max, arr[i])
--------return arr
39
Q

Given an integer array nums, move all the even integers at the beginning of the array followed by all the odd integers.

Return any array that satisfies this condition.

Example 1:

Input: nums = [3,1,2,4]
Output: [2,4,3,1]
Explanation: The outputs [4,2,3,1], [2,4,1,3], and [4,2,1,3] would also be accepted.

A
  1. two-pointer
  2. when even == odd, we are done!

time: O(n)
space: O(1)

class Solution:

  • —def sortArrayByParity(self, nums):
  • ——-even, odd = 0, len(nums) - 1
  • ——-while even < odd:
  • ———–if nums[even] % 2 != 0:
  • —————nums[even], nums[odd] = nums[odd], nums[even]
  • —————odd -= 1
  • ———–else:
  • —————even += 1
  • ——-return nums
40
Q
  1. Max Consecutive Ones II

Given a binary array nums, return the maximum number of consecutive 1’s in the array if you can flip at most one 0.

Example 1:

Input: nums = [1,0,1,1,0]
Output: 4
Explanation: Flip the first zero will get the maximum number of consecutive 1s. After flipping, the maximum number of consecutive 1s is 4.
Example 2:

Input: nums = [1,0,1,1,0,1]
Output: 4

A
  1. must be done in-place
  2. make a stream with rules, kind of state-functiony
  3. need to understand all edge cases, [0,0,0], [1,1,1, [0], [1], [1,0], [0,1]
  4. we need to return 1, 3, 1, 1, 2, 2, respectively
  5. understand regular case first [0, 1, 1, 0, 1, 1, 0, 1, 1, 1]
  6. get a current/prev count, on a zero, calc max + 1, prev = current, current = 0
  7. since we do a for loop, the last index isn’t always triggered how we want, so must check if we have “swapped”
  8. the swap boolean indicates how we calculate the final response. for 3 1’s, we never swap, so the answer is just 3, the cur ones, but if we have swapped, it is prev + cur ones + 1 since there is a 0 in there somewhere that wants to be swapped

time: O(n)
space: O(1)
class Solution:
—-def findMaxConsecutiveOnes(self, nums):
——–if len(nums) == 1 and nums[0] == 1:
————return 1
——–prev_ones = 0
——–cur_ones = 0
——–response = 0
——–swapped = False
——–for x in nums:
————if x == 0:
—————-response = max(cur_ones + prev_ones + 1, response)
—————-prev_ones, cur_ones = cur_ones, 0
—————-swapped = True
————else:
—————-cur_ones += 1
——–
——–if swapped:
————response = max(response, cur_ones + prev_ones + 1)
——–else:
————response = max(response, cur_ones)
——–return response

41
Q
  1. Third Maximum Number

Given an integer array nums, return the third distinct maximum number in this array. If the third maximum does not exist, return the maximum number.

Example 1:

Input: nums = [3,2,1]
Output: 1
Explanation:
The first distinct maximum is 3.
The second distinct maximum is 2.
The third distinct maximum is 1.
Example 2:
Input: nums = [1,2]
Output: 2
Explanation:
The first distinct maximum is 2.
The second distinct maximum is 1.
The third distinct maximum does not exist, so the maximum (2) is returned instead.
A
  1. dynamically build the max_set
  2. each time we have more than 3 values, remove the smallest value
  3. check if the max_set has less than 3 values, if it does we return the max (as per the problem statement)
  4. else, return the smallest in the set!

time: O(n)
space: O(4), bc we never have more than 4 elements in the set, so it is constant

class Solution:

  • —def thirdMax(self, nums):
  • ——-max_set = set()
  • ——-for x in nums:
  • ———–max_set.add(x)
  • ———–if len(max_set) > 3:
  • —————max_set.remove(min(max_set))
  • ——-if len(max_set) < 3:
  • ———–return max(max_set)
  • ——-else:
  • ———–return min(max_set)
42
Q
  1. Find All Numbers Disappeared in an Array

Given an array nums of n integers where nums[i] is in the range [1, n], return an array of all the integers in the range [1, n] that do not appear in nums.

Example 1:

Input: nums = [4,3,2,7,8,2,3,1]
Output: [5,6]
Example 2:

Input: nums = [1,1]
Output: [2]

A

brute force is to use N space. Make a hashmap on range 1, N inclusive, then loop again and check if they’re in the set, if they are remove it. All leftovers are the response

  1. since we know all the nums are in the range [1,n] inclusive, we can actually use that to constant time index this array and search for duplicates
  2. [1, 1, 1], we get the num 1, then go to the index abs(num) - 1 = 0 (which is technically the index related to 1 based on the params [1,N] values), if it isn’t negative, make it negative.
  3. ABSOLUTE VALUE bc if you make it negative then go back to it later, you get a negative index!!!
  4. loop again through the range of the list, if the arr[i] > 0, add i + 1 to response (dont forget it is i + 1, not just i)

time: O(n), we do 2 loops
space: O(1), no space except output

class Solution:

  • —def findDisappearedNumbers(self, nums):
  • ——-for i in range(len(nums)):
  • ———–index = abs(nums[i]) - 1
  • ———–if nums[index] > 0:
  • —————nums[index] *= -1
  • ——-response = []
  • ——-for i in range(len(nums)):
  • ———–if nums[i] > 0:
  • —————response.append(i + 1)
  • ——-return response
43
Q
  1. Squares of a Sorted Array

Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order.

Example 1:

Input: nums = [-4,-1,0,3,10]
Output: [0,1,9,16,100]
Explanation: After squaring, the array becomes [16,1,0,9,100].
After sorting, it becomes [0,1,9,16,100].
Example 2:

Input: nums = [-7,-3,2,3,11]
Output: [4,9,9,49,121]

A
  1. it is sorted, we know the square of a number is always positive, so compare absolute values of left/right index, then add those into a response array respectively in reverse order

time: O(n)
space: O(1) if we do not include response array

class Solution:

  • —def brute(self, nums):
  • ——-return sorted([x * x for x in nums])
  • —def sortedSquares(self, nums):
  • ——-response = [0] * len(nums)
  • ——-left, right = 0, len(nums) - 1
  • ——-for i in range(len(nums) - 1, -1, -1):
  • ———–if abs(nums[right]) >= abs(nums[left]):
  • —————current = nums[right]
  • —————right -= 1
  • ———–else:
  • —————current = nums[left]
  • —————left += 1
  • ———–response[i] = current * current
  • ——-return response
44
Q
  1. Find Minimum in Rotated Sorted Array

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.

A

brute force is linear time, just do a linear search and you can find it

  1. sorted, so we can probably do binary search, we just need to find the inflection point
  2. check if it has done a full rotation, if so return index 0, this allows us to do the custom binary search method
  3. inflection point is when all values on left < mid and mid > all on right. At this point the return is mid + 1

time: O(log(n)), binary search
space: O(1)

class Solution:

  • —def findMin(self, nums):
  • ——-if nums[0] <= nums[-1]:
  • ———–return nums[0]
  • ——-end = nums[-1]
  • ——-right = len(nums) - 1
  • ——-left = 0
  • ——-while right >= left:
  • ———–mid = (right + left) // 2
  • ———–if mid + 1 < len(nums) and nums[mid + 1] < nums[mid]:
  • —————return nums[mid + 1]
  • ———–if mid - 1 >= 0 and nums[mid - 1] > nums[mid]:
  • —————return nums[mid]
  • ———–# elif nums[mid] >= nums[0]:
  • ———–#—- left = mid + 1
  • ———–# else:
  • ———–#—- right = mid - 1
  • ———–elif nums[mid] <= end:
  • —————right = mid - 1
  • ———–else:
  • —————left = mid + 1
45
Q
  1. 3Sum

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.

Notice that the solution set must not contain duplicate triplets.

Example 1:

Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
Example 2:

Input: nums = []
Output: []

A
  1. think 2 sum with sorted list!
  2. iterate over the range, check if the value > 0, if so we’re done bc all the values left are positive and can never sum to 0
  3. check if index == 0 or the previous number != current, this checks to make sure we do not run duplicate 2 sums if there was an array that had [-1, -1, -1 or something
  4. we could use a set and check if the num[i] value has been seen before, but that adds N space
  5. do a standard 2 sum on each index, make sure low = i + 1, and while low < high since they cannot be equal indexes
  6. when you find a valid value, you add it to a response list. If you sort it then add it to the response list as a set, it still works. This prevents duplicate answers of equal value being added. if not you need to do the while loop

time: O(n^2), we iterate through the range and we sort the initial list, so n^2 +n log(n)
space: O(n) bc python uses N space for sorted/sort. If we implemented QS it would be logN

class Solution:

  • —def threeSum_twoSum(self, nums):
  • ——-# response = []
  • ——-response = set()
  • ——-nums.sort()
  • ——-for i in range(len(nums)):
  • ———–if nums[i] > 0:
  • —————break
  • ———–if i == 0 or nums[i] != nums[i - 1]:
  • —————self.two_sum(nums, response, i)
  • ——-return response
  • —def two_sum(self, nums, response, i):
  • ——-low, high = i + 1, len(nums) - 1
  • ——-while low < high:
  • ———–_sum = nums[i] + nums[low] + nums[high]
  • ———–if _sum < 0:
  • —————low += 1
  • ———–elif _sum > 0:
  • —————high -= 1
  • ———–else:
  • —————li = [nums[i], nums[low], nums[high]]
  • —————response.add(tuple(sorted(li)))
  • —————low += 1
  • —————high -= 1
  • —————# response.append(li)
  • —————# low += 1
  • —————# high -= 1
  • —————’’’
  • —————If you add a list instead of a set to response
  • —————you need to do this bc if you choose to use a list, there is a chance to add duplicates if you have [-1, -1, -1, 0, 1, 2]
  • —————you need to check backwards bc if you check forwards you can get a mistake with [-2,0,1,1,2], you get -2,0,2, but you
  • —————miss -2, 1, 1 since low == 2, high = 3, but low == low + 1
  • —————’’’
  • —————# while low < high and nums[low] == nums[low - 1]:
  • —————#—- low += 1
  1. this is if we cannot sort the initial list or change it, and we don’t want to make a copy of nums

time: O(n^2)
space: O(n) for the seen map

  • —def threeSum_no_sort(self, nums):
  • ——-response = set()
  • ——-seen = {}
  • ——-for i, val1 in enumerate(nums):
  • ———–for j, val2 in enumerate(nums[i + 1:], start = i + 1):
  • —————triplet_val = val1 + val2
  • —————zero_compliment = -triplet_val
  • —————’’’
  • —————[-1,0,1,2,-1,-4]
  • —————we need the seen[zero_compliment] == i check bc 2 is added to the seen map on a previous iteration
  • —————when we get to 2, -4, we have “seen” the zeros compliment 2 before, but it was on a previous iteration
  • —————that check helps us check against the previous iteration, this protects us against counting 1 values twice
  • —————’’’
  • —————if zero_compliment in seen and seen[zero_compliment] == i:
  • ——————-‘’’
  • ——————-sorted returns a list, so we need to use tuple() to cast it to a tuple
  • ——————-bc lists are not hashable (they are mutable, all mutable items not hashable)
  • ——————-‘’’
  • ——————-response.add(tuple(sorted([val1, val2, zero_compliment])))
  • —————seen[val2] = i
  • ——-return response
46
Q
  1. Container With Most Water

You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]).

Find two lines that together with the x-axis form a container, such that the container contains the most water.

Return the maximum amount of water a container can store.

Notice that you may not slant the container.

Example 1:

Input: height = [1,8,6,2,5,4,8,3,7]
Output: 49
Explanation: The above vertical lines are

A
  1. two pointer! walk through the proble, you will find the answer!

you do not need “smart next step calculations” outside of the current left/right
19, 600, 0, 0, 31, 19
if at 19/19 they are equal, and you go to 31 instead of 600, you only go off the min value, 19, then
you would move left up 1 and youd be at the same spot as before

19, 600, 0, 500, 0, 19
if you default and move right down 1, youll get 0, then move down to 500, which will lead to go from 19 to 600

the thing that may not be obvious is when you increment left/right, you might think there is an edge case that
if you don’t increment properly it will fail. The good thing is bc it checks each iteration whether to go right or left, you will
eventually get to the best area even if you just check the current level

time: O(n), we iterate through the list once
space: O(1), no extra space

class Solution:

  • —def maxArea(self, height):
  • ——-max_area = 0
  • ——-left, right = 0, len(height) - 1
  • ——-while left < right:
  • ———–h_left, h_right = height[left], height[right]
  • ———–_min = min(h_left, h_right)
  • ———–max_area = max(max_area, _min * (right - left))
  • ———–if h_left < h_right:
  • —————left += 1
  • ———–else:
  • —————right -= 1
  • ——-return max_area
47
Q
  1. Find All Duplicates in an Array

Given an integer array nums of length n where all the integers of nums are in the range [1, n] and each integer appears once or twice, return an array of all the integers that appears twice.

You must write an algorithm that runs in O(n) time and uses only constant extra space.

Example 1:

Input: nums = [4,3,2,7,8,2,3,1]
Output: [2,3]
Example 2:

Input: nums = [1,1,2]
Output: [1]

A

You can sort it or use a hashmap too as brute.

  1. similar to find all dissapeared nums in array
  2. use array itself to keep track of seen with indexing and negatives.
  3. loop, if arr[num - 1] (0th index) is positive, make it negative, if it is negative we have seen it before so add the actual number (absolute value number, not anum bc you probably made it abs(num) -1), add it to response

time: O(n), we do 1 loop, could be 3 if i did it my custom way
space: constant

class Solution:

  • —def optimal(self, nums):
  • ——-response = []
  • ——-for i, num in enumerate(nums):
  • ———–anum = abs(num) - 1
  • ———–if nums[anum] > 0:
  • —————nums[anum] *= -1
  • ———–else:
  • —————response.append(abs(num))
  • ——-return response
  • —def got_first_try(self, nums):
  • ——-for i, num in enumerate(nums):
  • ———–anum = abs(num) - 1
  • ———–if nums[anum] > 0:
  • —————nums[anum] *= -1
  • ——-for i, num in enumerate(nums):
  • ———–anum = abs(num) - 1
  • ———–nums[anum] *= -1
  • ——-return [i for i in range(1, len(nums) + 1) if nums[i - 1] < 0]
48
Q
  1. Flood Fill

An image is represented by an m x n integer grid image where image[i][j] represents the pixel value of the image.

You are also given three integers sr, sc, and newColor. You should perform a flood fill on the image starting from the pixel image[sr][sc].

To perform a flood fill, consider the starting pixel, plus any pixels connected 4-directionally to the starting pixel of the same color as the starting pixel, plus any pixels connected 4-directionally to those pixels (also with the same color), and so on. Replace the color of all of the aforementioned pixels with newColor.

Return the modified image after performing the flood fill.

Example 1:

Input: image = [[1,1,1],[1,1,0],[1,0,1]], sr = 1, sc = 1, newColor = 2
Output: [[2,2,2],[2,2,0],[2,0,1]]
Explanation: From the center of the image with position (sr, sc) = (1, 1) (i.e., the red pixel), all pixels connected by a path of the same color as the starting pixel (i.e., the blue pixels) are colored with the new color.
Note the bottom corner is not colored 2, because it is not 4-directionally connected to the starting pixel.

A
  1. DON’T FORGET TO CHECK BASECASE (what is the base case you ask?? if new color == flood color!!!)
  2. standard BFS solution with a queue. Queue can get as large as min(length, width), visualize a 2x100 and see it

time: O(n), the entire matrix could be 1 color
space: O(min(row, col))

from collections import deque
class Solution:
----def floodFill(self, image, sr: int, sc: int, newColor: int):
--------deltas = [(-1, 0), (1, 0), (0, -1), (0, 1)]
--------flood_color = image[sr][sc]
--------
--------if flood_color == newColor:
------------return image
--------
--------queue = deque()
--------queue.append((sr, sc))
--------image[sr][sc] = newColor
  • ——-while queue:
  • ———–row, col = queue.popleft()—-
  • ———–for dr, dc in deltas:
  • —————new_row = row + dr
  • —————new_col = col + dc
  • —————if 0 <= new_row < len(image) and 0 <= new_col < len(image[0]):
  • ——————-if image[new_row][new_col] == flood_color:
  • ———————–queue.append((new_row, new_col))
  • ———————–image[new_row][new_col] = newColor
  • ——-return image