N-All Flashcards
Given an n x n grid containing only values 0 and 1, where 0 represents water and 1 represents land, find a water cell such that its distance to the nearest land cell is maximized, and return the distance. If no land or water exists in the grid, return -1.
The distance used in this problem is the Manhattan distance: the distance between two cells (x0, y0) and (x1, y1) is |x0 - x1| + |y0 - y1|.
Input: grid = [[1,0,1],[0,0,0],[1,0,1]]
Output: 2
Explanation: The cell (1, 1) is as far as possible from all the land with distance 2.
class Solution: #Like walls and gates: multi-source bfs from all land tiles def maxDistance(self, grid: List[List[int]]) -> int: ROWS, COLS = len(grid), len(grid[0]) q = deque() visit = set() for r in range(ROWS): for c in range(COLS): if grid[r][c] == 1: q.append((r, c)) visit.add((r, c)) dist = -1 #based on how the math works out directions = [(-1, 0), (1, 0), (0, 1), (0, -1)] #we can maintain a dist here.. or not too! while q: for _ in range(len(q)): r, c = q.popleft() for dr, dc in directions: neiR, neiC = r + dr, c + dc if neiR >= 0 and neiR < ROWS and neiC >= 0 and neiC < COLS and (neiR, neiC) not in visit and grid[neiR][neiC] == 0: q.append((neiR, neiC)) visit.add((neiR, neiC)) dist += 1 #Why like this? We always increment dist by 1, so even if just iterate through land and add nothing -> -1 + 1 = 0 return -1 if dist == 0 else dist #Runtime: O(mn) #Space: O(mn)
You are given the root of a binary tree containing digits from 0 to 9 only.
Each root-to-leaf path in the tree represents a number.
For example, the root-to-leaf path 1 -> 2 -> 3 represents the number 123.
Return the total sum of all root-to-leaf numbers. Test cases are generated so that the answer will fit in a 32-bit integer.
A leaf node is a node with no children.
Input: root = [1,2,3]
Output: 25
Explanation:
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Therefore, sum = 12 + 13 = 25.
# Definition for a binary tree node. # class TreeNode: # def \_\_init\_\_(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def sumNumbers(self, root: Optional[TreeNode]) -> int: def dfs(node, path): if not node: return 0 path = path * 10 + node.val #This ordering of the above and below statements are important. Otherwise, we won't capture the leaf node value into the path! if not node.left and not node.right: return path return dfs(node.left, path) + dfs(node.right, path) return dfs(root, 0) #Runtime: O(n) #Space: O(h) #Idea: When a leaf node returns, it will return the value #of the path from the root to it. #So when a parent looks and its 2 children (even if parent) is in the middle of tree, it will see the path values #from root to its two children. It then takes the sum of #those two path values and return those.
You are given an array people where people[i] is the weight of the ith person, and an infinite number of boats where each boat can carry a maximum weight of limit. Each boat carries at most two people at the same time, provided the sum of the weight of those people is at most limit.
Return the minimum number of boats to carry every given person.
Input: people = [3,5,3,4], limit = 5
Output: 4
Explanation: 4 boats (3), (3), (4), (5)
class Solution: def numRescueBoats(self, people: List[int], limit: int) -> int: people.sort() res = 0 l, r = 0, len(people) - 1 while l <= r: #We will take the heaviest person for sure res += 1 remainingWeight = limit - people[r] r -= 1 #Take the lightest person if possible, and only if pointers have not crossed. #Example [1, 2]: At this point l and r are both at i = 0, so that's fine. We can still take 1 #Example [1]. At this point, r is -1, so cannot possibly take l. if l <= r and remainingWeight >= people[l]: l += 1 return res #O(n log n) for sorting, O(n) for traversal #O(1) pointers
Given an integer n, break it into the sum of k positive integers, where k >= 2, and maximize the product of those integers.
Return the maximum product you can get.
Input: n = 10
Output: 36
Explanation: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36.
class Solution: def integerBreak(self, n: int) -> int: dp = {1: 1} for num in range(2, n + 1): #We are required to break n up, so max result so far is zero. #Otherwise if not n, we don't have to break num up since that can #potentially be the max, and further subdivisions will worsen our result dp[num] = 0 if num == n else num #Two pieces: i and num - i #i goes to num not num + 1! The reason is we want to disallow #Breaking into (0, num). for i in range(1, num): val = dp[i] * dp[num - i] dp[num] = max(dp[num], val) return dp[n] #Runtime: O(n ^ 2): n num subproblems 1 through n, each num takes O(n) time for the breakage into two pieces #Space: O(n) for the dp cache
You are given an integer n, the number of nodes in a directed graph where the nodes are labeled from 0 to n - 1. Each edge is red or blue in this graph, and there could be self-edges and parallel edges.
You are given two arrays redEdges and blueEdges where:
redEdges[i] = [ai, bi] indicates that there is a directed red edge from node ai to node bi in the graph, and
blueEdges[j] = [uj, vj] indicates that there is a directed blue edge from node uj to node vj in the graph.
Return an array answer of length n, where each answer[x] is the length of the shortest path from node 0 to node x such that the edge colors alternate along the path, or -1 if such a path does not exist.
Input: n = 3, redEdges = [[0,1],[1,2]], blueEdges = []
Output: [0,1,-1]
class Solution: def shortestAlternatingPaths(self, n: int, redEdges: List[List[int]], blueEdges: List[List[int]]) -> List[int]: redAdj = defaultdict(list) #maps a node to the red edge neighbors blueAdj = defaultdict(list) #maps node to blue for u, v in redEdges: redAdj[u].append(v) for u, v in blueEdges: blueAdj[u].append(v) visit = set() #(node, incoming color) visit.add((0, None)) q = deque() #(node, incoming color, path length) q.append((0, None, 0)) answer = [-1] * n answer[0] = 0 while q: for _ in range(len(q)): curNode, prevColor, curLength = q.popleft() #If first time ever visiting this node, that's the minimum if answer[curNode] == -1: answer[curNode] = curLength #For source, prevColor is None, so both will fire. if prevColor != "RED": for v in redAdj[curNode]: if (v, "RED") not in visit: visit.add((v, "RED")) q.append((v, "RED", curLength + 1)) if prevColor != "BLUE": for v in blueAdj[curNode]: if (v, "BLUE") not in visit: visit.add((v, "BLUE")) q.append((v, "BLUE", curLength + 1)) return answer #O(2*N) -> O(N) #O(2*N -> O(N)
Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k.
A subarray is a contiguous non-empty sequence of elements within an array.
Input: nums = [1,1,1], k = 2
Output: 2
class Solution: def subarraySum(self, nums: List[int], k: int) -> int: prefixCounts = {0: 1} #for prefixSum of 0, there's 1 way at least. curSum = 0 res = 0 for n in nums: curSum += n diff = curSum - k res += prefixCounts.get(diff, 0) prefixCounts[curSum] = prefixCounts.get(curSum, 0) + 1 return res #Runtime: O(n) to runthrough all elts and just do a check if diff is inside. #Space: O(n) for the prefixCounts map
We are given an array asteroids of integers representing asteroids in a row.
For each asteroid, the absolute value represents its size, and the sign represents its direction (positive meaning right, negative meaning left). Each asteroid moves at the same speed.
Find out the state of the asteroids after all collisions. If two asteroids meet, the smaller one will explode. If both are the same size, both will explode. Two asteroids moving in the same direction will never meet.
Input: asteroids = [5,10,-5]
Output: [5,10]
Explanation: The 10 and -5 collide resulting in 10. The 5 and 10 never collide.
class Solution: def asteroidCollision(self, asteroids: List[int]) -> List[int]: stack = [] for curAst in asteroids: while stack and stack[-1] > 0 and curAst < 0: diff = stack[-1] + curAst if diff < 0: stack.pop() elif diff > 0: curAst = 0 #curAst is destroyed so set it to zero else: curAst = 0 #curAst is destroyed stack.pop() #both are destroyed if curAst: #if we still have an asteroid as a result of these operations stack.append(curAst) return stack #Runtime: O(n) #Space: O(n)
You are given an m x n integer array grid. There is a robot initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time.
An obstacle and space are marked as 1 or 0 respectively in grid. A path that the robot takes cannot include any square that is an obstacle.
Return the number of possible unique paths that the robot can take to reach the bottom-right corner.
The testcases are generated so that the answer will be less than or equal to 2 * 109.
Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
Output: 2
Explanation: There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right
class Solution: def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: ROWS, COLS = len(obstacleGrid), len(obstacleGrid[0]) dp = [0] * COLS #Think of the dp array as one row below the grid. So the loop will need to start from the last row and go upwards. But we do prefill the last entry with a 0 as base case. dp[COLS - 1] = 1 #1 way to get from itself to destination for r in range(ROWS - 1, -1, -1): for c in range(COLS - 1, -1, -1): if obstacleGrid[r][c]: #obstacle is if it's a 1. dp[c] = 0 #immediately zero out. There's no way to get from this square to the bottom right. #The current entry reflects the cell below. So just potentially add the cell from the right elif c + 1 < COLS: #not COLS - 1. dp[c] += dp[c + 1] return dp[0] #Runtime: O(mn) #Space: O(n) single row array needed.
Unique Paths (Single Array Solution)
There is a robot on an m x n grid. The robot is initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time.
Given the two integers m and n, return the number of possible unique paths that the robot can take to reach the bottom-right corner.
The test cases are generated so that the answer will be less than or equal to 2 * 109.
Input: m = 3, n = 2
Output: 3
Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Down -> Down
2. Down -> Down -> Right
3. Down -> Right -> Down
class Solution: def uniquePaths(self, m: int, n: int) -> int: dp = [0] * n #reflects the row below the grid dp[n - 1] = 1 #base case: number of ways to reach bottom right from bottom right is 1 for r in range(m - 1, -1, -1): for c in range(n - 1, -1, -1): #dp[c] contains the number of ways from below already! (in-place) #Potentially add number of ways from the right, if in bounds if c + 1 < n: dp[c] += dp[c + 1] return dp[0] #Runtime: O(mn) #Space: O(n) one array only.
783. Minimum Distance Between BST Nodes
Given the root of a Binary Search Tree (BST), return the minimum difference between the values of any two different nodes in the tree.
Input: root = [4,2,6,1,3]
Output: 1
# Definition for a binary tree node. # class TreeNode: # def \_\_init\_\_(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def minDiffInBST(self, root: Optional[TreeNode]) -> int: prev, res = None, float('inf') #prev is the prev node. #This forms an IOT. def dfs(node): if not node: return dfs(node.left) nonlocal prev, res #Since these are just variables, have to set these. #On the very first IOT call, res is not yet set since prev is None #But then prev will later be set to node corresponding to 1. #Then when popped back up, after dfs(node.left) is completed, prev corresponds to 1, so comparison will be made with 2, etc. if prev: res = min(res, node.val - prev.val) prev = node dfs(node.right) dfs(root) return res #O(n) #O(h)
Decode String
Given an encoded string, return its decoded string.
The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer.
You may assume that the input string is always valid; there are no extra white spaces, square brackets are well-formed, etc. Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there will not be input like 3a or 2[4].
The test cases are generated so that the length of the output will never exceed 105.
Input: s = “3[a2[c]]”
Output: “accaccacc”
class Solution: def decodeString(self, s: str) -> str: stack = [] for i in range(len(s)): if s[i] != "]": stack.append(s[i]) else: #is "]" #Phase 1: Get the string until we see a left brace sub = "" while stack[-1] != "[": sub = stack.pop() + sub #Pop the "[" stack.pop() #Phase 2: Get the count count = "" while stack and stack[-1] in "0123456789": count = stack.pop() + count stack.append((int(count) * sub)) return "".join(stack) #Runtime: O(sum of the numbers in string * n) #e.g. 1000[ab] #Space: O(n) for stack
Design HashMap
Design a HashMap without using any built-in hash table libraries.
Implement the MyHashMap class:
MyHashMap() initializes the object with an empty map.
void put(int key, int value) inserts a (key, value) pair into the HashMap. If the key already exists in the map, update the corresponding value.
int get(int key) returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key.
void remove(key) removes the key and its corresponding value if the map contains the mapping for the key.
Input
[“MyHashMap”, “put”, “put”, “get”, “get”, “put”, “get”, “remove”, “get”]
[[], [1, 1], [2, 2], [1], [3], [2, 1], [2], [2], [2]]
Output
[null, null, null, 1, -1, null, 1, null, -1]
Explanation
MyHashMap myHashMap = new MyHashMap();
myHashMap.put(1, 1); // The map is now [[1,1]]
myHashMap.put(2, 2); // The map is now [[1,1], [2,2]]
myHashMap.get(1); // return 1, The map is now [[1,1], [2,2]]
myHashMap.get(3); // return -1 (i.e., not found), The map is now [[1,1], [2,2]]
myHashMap.put(2, 1); // The map is now [[1,1], [2,1]] (i.e., update the existing value)
myHashMap.get(2); // return 1, The map is now [[1,1], [2,1]]
myHashMap.remove(2); // remove the mapping for 2, The map is now [[1,1]]
myHashMap.get(2); // return -1 (i.e., not found), The map is now [[1,1]]
class ListNode: def \_\_init\_\_(self, key = -1, val = -1, nxt = None): #Default values above self.key = key self.val = val self.next = nxt class MyHashMap: def \_\_init\_\_(self): self.map = [ListNode() for i in range(1000)] def getHash(self, key): return key % len(self.map) def put(self, key: int, value: int) -> None: cur = self.map[self.getHash(key)] while cur and cur.next: if cur.next.key == key: cur.next.val = value return cur = cur.next #At the final node cur.next = ListNode(key, value) def get(self, key: int) -> int: cur = self.map[self.getHash(key)] while cur: if cur.key == key: return cur.val cur = cur.next return -1 def remove(self, key: int) -> None: cur = self.map[self.getHash(key)] while cur and cur.next: if cur.next.key == key: cur.next = cur.next.next cur = cur.next #Runtime: O(1) usually, or O(N) if long chained at an index #Space: O(N) for hashmap Your MyHashMap object will be instantiated and called as such: # obj = MyHashMap() # obj.put(key,value) # param_2 = obj.get(key) # obj.remove(key)
Trim a Binary Search Tree
Given the root of a binary search tree and the lowest and highest boundaries as low and high, trim the tree so that all its elements lies in [low, high]. Trimming the tree should not change the relative structure of the elements that will remain in the tree (i.e., any node’s descendant should remain a descendant). It can be proven that there is a unique answer.
Return the root of the trimmed binary search tree. Note that the root may change depending on the given bounds.
Input: root = [1,0,2], low = 1, high = 2
Output: [1,null,2]
# Definition for a binary tree node. # class TreeNode: # def \_\_init\_\_(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]: if not root: return None if root.val > high: return self.trimBST(root.left, low, high) elif root.val < low: return self.trimBST(root.right, low, high) else: root.left = self.trimBST(root.left, low, high) root.right = self.trimBST(root.right, low, high) return root #O(N) #O(H)
Unique Binary Search Trees
Given an integer n, return the number of structurally unique BST’s (binary search trees) which has exactly n nodes of unique values from 1 to n.
Input: n = 3
Output: 5
class Solution: def numTrees(self, n: int) -> int: dp = [0] * (n + 1) dp[0] = 1 dp[1] = 1 for numNodes in range(2, n + 1): total = 0 for root in range(1, numNodes + 1): left = root - 1 #so if our root is value 1, then the node 0 is on the left right = numNodes - root total += dp[left] * dp[right] dp[numNodes] = total return dp[n] #O(n^2) #O(n) #So if 3 nodes total #2 nodes to the right. 3 - 1
Remove Duplicates from Sorted List
Given the head of a sorted linked list, delete all duplicates such that each element appears only once. Return the linked list sorted as well.
Input: head = [1,1,2,3,3]
Output: [1,2,3]
# Definition for singly-linked list. # class ListNode: # def \_\_init\_\_(self, val=0, next=None): # self.val = val # self.next = next class Solution: def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: cur = head while cur: while cur.next and cur.next.val == cur.val: cur.next = cur.next.next cur = cur.next return head #O(n) #O(1)
Robot Bounded In Circle
On an infinite plane, a robot initially stands at (0, 0) and faces north. Note that:
The north direction is the positive direction of the y-axis.
The south direction is the negative direction of the y-axis.
The east direction is the positive direction of the x-axis.
The west direction is the negative direction of the x-axis.
The robot can receive one of three instructions:
“G”: go straight 1 unit.
“L”: turn 90 degrees to the left (i.e., anti-clockwise direction).
“R”: turn 90 degrees to the right (i.e., clockwise direction).
The robot performs the instructions given in order, and repeats them forever.
Return true if and only if there exists a circle in the plane such that the robot never leaves the circle.
Input: instructions = “GGLLGG”
Output: true
Explanation: The robot is initially at (0, 0) facing the north direction.
“G”: move one step. Position: (0, 1). Direction: North.
“G”: move one step. Position: (0, 2). Direction: North.
“L”: turn 90 degrees anti-clockwise. Position: (0, 2). Direction: West.
“L”: turn 90 degrees anti-clockwise. Position: (0, 2). Direction: South.
“G”: move one step. Position: (0, 1). Direction: South.
“G”: move one step. Position: (0, 0). Direction: South.
Repeating the instructions, the robot goes into the cycle: (0, 0) –> (0, 1) –> (0, 2) –> (0, 1) –> (0, 0).
Based on that, we return true.
class Solution: def isRobotBounded(self, instructions: str) -> bool: #We are bounded if #Position never changes #Position changes AND direction changes dx, dy = 0, 1 x, y = 0, 0 for c in instructions: if c == "G": x, y = x + dx, y + dy elif c == "L": #Example: 0, 1 becomes -1, 0 dx, dy = -dy, dx else: #it's an R #Example: 1, 0 becomes 0, -1 dx, dy = dy, -dx return (x, y) == (0, 0) or (dx, dy) != (0, 1) #When the second statement is executed, (x, y) is not (0, 0). No need to include that in that condition. #Runtime O(n) #Space: O(1)
Maximum Sum Circular Subarray
Given a circular integer array nums of length n, return the maximum possible sum of a non-empty subarray of nums.
A circular array means the end of the array connects to the beginning of the array. Formally, the next element of nums[i] is nums[(i + 1) % n] and the previous element of nums[i] is nums[(i - 1 + n) % n].
A subarray may only include each element of the fixed buffer nums at most once. Formally, for a subarray nums[i], nums[i + 1], …, nums[j], there does not exist i <= k1, k2 <= j with k1 % n == k2 % n.
Input: nums = [1,-2,3,-2]
Output: 3
Explanation: Subarray [3] has maximum sum 3.
class Solution: def maxSubarraySumCircular(self, nums: List[int]) -> int: curMax = 0 globalMax = nums[0] curMin = 0 globalMin = nums[0] total = 0 #If it's circular... We either take the global max, or we take the sum of the whole array and subtract out the globalMin for n in nums: total += n curMax = max(curMax + n, n) globalMax = max(curMax, globalMax) curMin = min(curMin + n, n) globalMin = min(curMin, globalMin) #What does our current code do if all negative? We take the sum of all the values (-8), subtract by curMin = -8, to get a value of 0 which is incorrect #The cause of the above issue is that globalMax is negative. So in the event of all negatives, globalMax is the biggest negative number. We can just then return that globalMax negative number. return max(globalMax, total - globalMin) if globalMax > 0 else globalMax #Runtime: O(n) #Space: O(1)
Middle of the Linked List
Given the head of a singly linked list, return the middle node of the linked list.
If there are two middle nodes, return the second middle node.
Input: head = [1,2,3,4,5,6]
Output: [4,5,6]
Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one.
# Definition for singly-linked list. # class ListNode: # def \_\_init\_\_(self, val=0, next=None): # self.val = val # self.next = next class Solution: def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next return slow #O(n) #O(1) #Second middle node: slow, fast = head, head #First middle node: slow, fast = head, head.next
Minimum Size Subarray Sum
Given an array of positive integers nums and a positive integer target, return the minimal length of a
subarray
whose sum is greater than or equal to target. If there is no such subarray, return 0 instead.
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.
class Solution: def minSubArrayLen(self, target: int, nums: List[int]) -> int: res = float('inf') curSum = 0 l = 0 for r in range(len(nums)): curSum += nums[r] while curSum >= target: res = min(res, r - l + 1) curSum -= nums[l] l += 1 return res if res != float('inf') else 0 #O(n) #O(1)
You are keeping the scores for a baseball game with strange rules. At the beginning of the game, you start with an empty record.
You are given a list of strings operations, where operations[i] is the ith operation you must apply to the record and is one of the following:
An integer x.
Record a new score of x.
‘+’.
Record a new score that is the sum of the previous two scores.
‘D’.
Record a new score that is the double of the previous score.
‘C’.
Invalidate the previous score, removing it from the record.
Return the sum of all the scores on the record after applying all the operations.
The test cases are generated such that the answer and all intermediate calculations fit in a 32-bit integer and that all operations are valid.
Input: ops = [“5”,”2”,”C”,”D”,”+”]
Output: 30
Explanation:
“5” - Add 5 to the record, record is now [5].
“2” - Add 2 to the record, record is now [5, 2].
“C” - Invalidate and remove the previous score, record is now [5].
“D” - Add 2 * 5 = 10 to the record, record is now [5, 10].
“+” - Add 5 + 10 = 15 to the record, record is now [5, 10, 15].
The total sum is 5 + 10 + 15 = 30.
class Solution: def calPoints(self, operations: List[str]) -> int: stack = [] for op in operations: if op == "+": stack.append(stack[-1] + stack[-2]) elif op == "D": stack.append(2 * stack[-1]) elif op == "C": stack.pop() else: stack.append(int(op)) return sum(stack) #O(n) #O(n)
Valid Perfect Square
Given a positive integer num, return true if num is a perfect square or false otherwise.
A perfect square is an integer that is the square of an integer. In other words, it is the product of some integer with itself.
You must not use any built-in library function, such as sqrt.
Input: num = 16
Output: true
Explanation: We return true because 4 * 4 = 16 and 4 is an integer.
class Solution: def isPerfectSquare(self, num: int) -> bool: l, r = 1, num while l <= r: mid = l + (r - l) // 2 squared = mid ** 2 if squared == num: return True elif squared > num: r = mid - 1 else: l = mid + 1 return False #O(log num) #O(1)
First Missing Positive
Given an unsorted integer array nums, return the smallest missing positive integer.
You must implement an algorithm that runs in O(n) time and uses constant extra space.
Input: nums = [3,4,-1,1]
Output: 2
Explanation: 1 is in the array but 2 is missing.
class Solution: def firstMissingPositive(self, nums: List[int]) -> int: #Mark negative values with a zero for i in range(len(nums)): if nums[i] < 0: nums[i] = 0 #Mark idx val - 1 with a negative, if in bounds. for i in range(len(nums)): idx = abs(nums[i]) - 1 if idx >= 0 and idx < len(nums): #If the target to negate is a 0, mark it as -(len(nums) + 1) if nums[idx] == 0: nums[idx] = -(len(nums) + 1) else: nums[idx] = -abs(nums[idx]) #iterate through 1... len(A) and see which one is missing. This won't go out of bounds, since this is a predefined solution set, so no need to check OOB. for i in range(1, len(nums) + 1): idx = i - 1 if nums[idx] >= 0: return i return len(nums) + 1 #Runtime: O(n) #Space: O(1) #modify input array # We got to the 1 dx, check if that value is NEGATIVE. If it is negative, 2 exists in the input array But what if negative values in array? We don't care about negative values. We'll replace them with a zero. Solution set is bounded within [1..len(A)] Example: [123] -> 4 1) Pre-work: Iterate through the array, mark negative values with a 0. This is because we don't care about negative values at all. # 2) Traverse through the input array, and for every val, mark slot for idx i = val - 1 as visited with a negative, if it's in bounds. # #To account for edge cases for which a negative value was flipped to a 0, we can instead flip it to a negative value that is not # #within [1..len(A)]. Let's do -(len(A) + 1), because index len(A) is out of bounds! # 3) Traverse through [1... len(A)]. For each value here, check if slot for idx = val - 1 is negative. If it's non-negative, immediately return the val. # #If the loop terminates and still not returned, return len(A) + 1. # #Example: [123] -> [-1 -2 -3], so 4.
Number of Distinct Islands
You are given an m x n binary matrix grid. An island is a group of 1’s (representing land) connected 4-directionally (horizontal or vertical.) You may assume all four edges of the grid are surrounded by water.
An island is considered to be the same as another if and only if one island can be translated (and not rotated or reflected) to equal the other.
Return the number of distinct islands.
Input: grid = [[1,1,0,1,1],[1,0,0,0,0],[0,0,0,0,1],[1,1,0,1,1]]
Output: 3
Each recursive pop-up has a “B” attached to it. The total number of “B” is equal to the size of the recursion stack.. the number of cells in the island.
class Solution: def numDistinctIslands(self, grid: List[List[int]]) -> int: ROWS, COLS = len(grid), len(grid[0]) visit = set() uniqueIslands = set() def dfs(r, c, direction): if r < 0 or r == ROWS or c < 0 or c == COLS or (r, c) in visit or grid[r][c] == 0: return visit.add((r, c)) curPath.append(direction) dfs(r + 1, c, "D") dfs(r - 1, c, "U") dfs(r, c - 1, "L") dfs(r, c + 1, "R") curPath.append("B") #backtracked to disambiguate for r in range(ROWS): for c in range(COLS): if grid[r][c] == 1 and (r, c) not in visit: curPath = [] dfs(r, c, "S") #Start if curPath: uniqueIslands.add(tuple(curPath)) return len(uniqueIslands) #Runtime: O(MN) for running DFS from every island, etc. #Space: O(MN) for visit set, and also much larger than the path signature set. # Each recursive pop-up has a "B" attached to it. The total number of "B" is equal to the size of the recursion stack.. the number of cells in the island. # grid = # [[1,1,0,0,0],[1,1,0,0,0],[0,0,0,1,1],[0,0,0,1,1]] # Output = 1 # {('START', 'D', 'R', 'U', 'B', 'B', 'B', 'B')} [[1,1,0,1,1],[1,0,0,0,0],[0,0,0,0,1],[1,1,0,1,1]] # Output: 3 # {('START', 'D', 'L', 'B', 'B', 'B'), ('START', 'D', 'B', 'R', 'B', 'B'), ('START', 'R', 'B', 'B')}
Count Primes
Given an integer n, return the number of prime numbers that are strictly less than n.
Input: n = 10
Output: 4
Explanation: There are 4 prime numbers less than 10, they are 2, 3, 5, 7.
class Solution: def countPrimes(self, n: int) -> int: # res = 0 # #0 and 1 are not prime, we know that. # for num in range(2, n): # isPrime = True # for j in range(2, num): # if num % j == 0: # isPrime = False # if isPrime: # res += 1 # return res if n <= 2: #0 and 1 are not prime return 0 primes = [True] * n primes[0] = False primes[1] = False #Assume prime by default for p in range(2, int(n ** 0.5) + 1): #we don't need to go until n. At most 0 to sqrt n. So we do (sqrt n) + 1 to exclude the +1 part of it. if primes[p]: for multiple in range(p * p, n, p): primes[multiple] = False return sum(primes) #Sum up the entries in primes that are True #Runtime: O(sqrt(n) * log log n) #log log n is n /2 + n / 3 + n / 5 + ... + n/(last prime < n) #Space: O(n) for primes array