LeetCode
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. Example 1: Input: nums = [1,2,3,1] Output: true
USE HASHSET Create a hashset of values seen so far while iterating through if current value is in seen, return true else add value to see if loop finishes, return false
Invert Binary Tree Given the root of a binary tree, invert the tree, and return its root. Example 1: Input: root = [4,2,7,1,3,6,9] Output: [4,7,2,9,6,3,1]
USE DFS recursively search down the right and left nodes of each root and swap them return the root if its null or swap is complete
Number of Islands Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water. Example 1: Input: grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] Output: 1
USE DFS OF GRAPH Create a visited 2d array and have all values as false (in python for an MxN array, use [[False]*n for _ in range(m)] to init array) Go through all values of input grid and dfs when you reach an unvisited '1' increment count of islands each time to dfs - mark current node as visited - visited all up, down, left, right neighbors that are also unvisited and '1'
Number of Islands Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water. Example 1: Input: grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] Output: 1
USE AN ADJACENCY GRAPH APPROACH make a visited[][] and helper method that recursively visits any node that isn't visited and is part of the island our goal is to visit every connected component of a node and call that an island to do so, iterate through the 2D grid and look for any land mass that has yet to be visited. Once found, call the private helper method to mark every connected component as visited. Now increment count and continue the loop. once the loop is done, every land mass will be visited and each unique island counted.
Combination Sum Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order. The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different. The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input. Example 1: Input: candidates = [2,3,6,7], target = 7 Output: [[2,2,3],[7]] Explanation: 2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times. 7 is a candidate, and 7 = 7. These are the only two combinations.
USE BACKTRACKING Basic setup: - list of lists that add up to target - sort nums array backtrack: - if remain < 0, return (base case when you've overshot the target) - if remain == 0, add the current list of nums to answer list (base case when sum of current list == target) otherwise, - iterate from current index in nums to end - add nums[new_idx] to current list of nums and backtrack - remove the last entry in current list and repeat return list of lists
Merge Intervals Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input. Example 1: Input: intervals = [[1,3],[2,6],[8,10],[15,18]] Output: [[1,6],[8,10],[15,18]] Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].
USE SORTING to sort based on start times use Arrays.sort(intervals, (a,b)->Integer.compare(a[0], b[0])); then create an ArrayList<int[]> to store the merged int arrays. Create and add to that list a newInterval int array that corresponds to intervals[0] now while iterating through all the intervals, compare the end time of that interval with the start time of the new interval -> if this start time is ≤ the end time, set the newInterval's end time to the max of either interval -> otherwise, set newInterval to check against as the current one being visited and add that to the list (so the algo only adds intervals after they have been as merged as possible) return the list as an int[] by doing merged.toArray(new int[merged.size()][])
Palindrome Partitioning Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s. Example 1: Input: s = "aab" Output: [["a","a","b"],["aa","b"]]
USE BACKTRACKING Keep track of the current partitions (only added if its a palindrome) backtrack - check if idx is == size(s), add list of partitions to result list - iterate from idx to end, check if current slice s[idx:end+1] is a palindrome - > if yes, add slice to partitions and check the remaining substring of s
Letter Combinations of a Phone Number Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. Return the answer in any order. A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters. Example 1: Input: digits = "23" Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
USE BACKTRACKING populate list with all number to letter mapping backtrack - if idx == len(digits), add current combo to result and return - iterate through all the letters matching digits[idx] and backtrack
Number of Connected Components in an Undirected Graph There is an undirected graph with n nodes. There is also an edges array, where edges[i] = [a, b] means that there is an edge between node a and node b in the graph. The nodes are numbered from 0 to n - 1. Return the total number of connected components in that graph. Example 1: Input: n=3 edges=[[0,1], [0,2]] Output: 1 Copy Example 2: Input: n=6 edges=[[0,1], [1,2], [2,3], [4,5]] Output: 2
USE DFS dfs: - check if alr visited - otherwise explore all unvisited children loop through all unvisited nodes and explore them, add to count each time return count
Group Anagrams Given an array of strings strs, group the anagrams together. You can return the answer in any order. An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once. Example 1: Input: strs = ["eat","tea","tan","ate","nat","bat"] Output: [["bat"],["nat","tan"],["ate","eat","tea"]]
USE HASHMAP WITH SORTED STRING AS KEY create a HashMap that maps a sorted string to an anagram. iterate through the list of strings convert the string into a char[] which we can sort later by using str.toCharArray() sort char[] and convert it back into a string by using sortedStr = String.valueOf(char []) check if map containsKey(sortedStr) and if it doesn't, make new list add str to list at map.get(sortedStr) return list of lists
Range Addition Assume you have an array of length n initialized with all 0's and are given k update operations. Each operation is represented as a triplet: [startIndex, endIndex, inc] which increments each element of subarray A[startIndex ... endIndex] (startIndex and endIndex inclusive) with inc. Return the modified array after all k operations were executed. Example: Input: length = 5, updates = [[1,3,2],[2,4,3],[0,2,-2]] Output: [-2,0,3,5,3] Explanation: Initial state: [0,0,0,0,0] After applying operation [1,3,2]: [0,2,2,2,0] After applying operation [2,4,3]: [0,2,5,5,3] After applying operation [0,2,-2]: [-2,0,3,5,3]
USE PREFIX ARRAY Create an array of length n that keeps track of all changes to a variable called increment - (1,3,2) means at index 1 you update increment by +2 and at index 4 update increment by -2 Iterate through initial array and update values based on increment[i]
Reverse a Linked List Given the head of a singly linked list, reverse the list, and return the reversed list. 1->2->3->4->5 5->4->3->2->1 Example 1: Input: head = [1,2,3,4,5] Output: [5,4,3,2,1]
USE RECURSION AND RECURRENCE RELATIONS base case: if head == null or head.next == null rec case: create a new listnode that will be the previous node by doing p = reverseList(head.next) assign that nodes .next as the head node (head.next.next = head;) now erase the connection from head to head.next (head.next = null;) return p to give a reversed version of head and head.next
Best Time to Buy or Sell 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.
USE RUNNING MAX AND MIN while iterating, keep track of the lowest price, the maxProfit check if the current price is less than lowest price if yes, then set lowest price to current price otherwise, if the current price - lowest price is > than maxProfit, set maxProfit to that value return maxProfit after all iterations
Maximum 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 subarray is a contiguous part of an array. Example 1: Input: nums = [-2,1,-3,4,-1,2,1,-5,4] Output: 6 Explanation: [4,-1,2,1] has the largest sum = 6.
USE RUNNING SUM AND MAX SUM keep track of the current sum and the max sum by using Math.max to track the current subarray's sum, set sum = Math.max(sum + nums[i], nums[i]) (which sets the sum to the continuous subarray or restarts the subarray based on which value is bigger) to track the current max sum, set max = Math.max(max, sum) (which sets the max to the biggest subarray sum we've seen as the current sum may be smaller than one before it) return the max
Buddy Strings Given two strings s and goal, return true if you can swap two letters in s so the result is equal to goal, otherwise, return false. Swapping letters is defined as taking two indices i and j (0-indexed) such that i != j and swapping the characters at s[i] and s[j]. For example, swapping at indices 0 and 2 in "abcd" results in "cbad". Example 1: Input: s = "ab", goal = "ba" Output: true Explanation: You can swap s[0] = 'a' and s[1] = 'b' to get "ba", which is equal to goal.
USE SETS & CHECK DIFFERENCES If the lengths are different return False If the strings are the same, return true or false based on the len(set(str1)) being less than len(str2) -> this means there are repeat characters in the strings that can be swapped otherwise make a list(tuple()) of differences between the strings, if the list is != 2 then return False. if the elements are the same when one is reversed, return True.
Longest Substring Without Repeating Characters Given a string s, find the length of the longest substring without repeating characters. Example 1: Input: s = "abcabcbb" Output: 3 Explanation: The answer is "abc", with the length of 3.
USE TWO POINTER AND HASHMAP We must create a hashmap (or an ascii char[] of size 256) that maps characters in the string to their index. The outer for loop will iterate through the string incrementing the right pointer. In each iteration, check if (not a while loop) the curr char is already in the hashmap and update the left pointer to be the max of (j, map.get(currChar) + 1 update map.put(currChar, i) update maxlength to (max, right - left + 1) return max after loop finishes
Jump Game You are given an integer array nums. You are initially positioned at the array's first index, and each element in the array represents your maximum jump length at that position. Return true if you can reach the last index, or false otherwise. Example 1: Input: nums = [2,3,1,1,4] Output: true Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
CHECK REACH The idea is to see if your reach from a certain index can reach the goal. When working backwards, this means keep index n-1 as your goal and check if n-2 + # of jump spaces can reach goal. Then update n-2 as your new goal since you've confirmed that n-2 can reach the real goal. At the end, return true if goal is 0 When working forwards, this means keeping track of what the furthest you can reach is starting from 0. Iterate through every index. If reach < index, then you can't reach this space so return False. Update reach to be the max of the current reach or the # of jump spaces. At the end, return true.
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.
DIVIDE AND CONQUER Because we want an O(log n) alg, we have to cut our problem in half recursively, To find min, we need to keep track of the start and end of our subarray if we are comparing 2 or fewer nums, return the smaller or the singular otherwise find the mid of the subarray (use i + (j - i)/2 to avoid integer overflow) if the number in the mid is smaller than both the number to its left and end of the subarray, then you've found the smallest num so return that otherwise, if the num at mid - 1 is bigger than the number at num[end], search from mid+1 to end or search from start to mid-1
Palindromic Substrings Given a string s, return the number of palindromic substrings in it. A string is a palindrome when it reads the same backward as forward. A substring is a contiguous sequence of characters within the string. Example 1: Input: s = "abc" Output: 3 Explanation: Three palindromic strings: "a", "b", "c". Example 2: Input: s = "aaa" Output: 6 Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".
The idea is to check how many palindromes you can make by extending from each character. for example: for string aaa a count++ then check a(a)a (a was already checked so only need to check that i - 1 == i + 1) count++ return 2 from this single a. to implement this: have an outer loop that iterates through each character in the string from each character, have a for loop that has 2 pointers that go out left and right from i (acting as the middle of the palindrome) so naturally, you need one more inner for loop that goes left and right but right starts as i + 1 to keep track of even palindromes in both inner loops, check that left and right are in bounds and the characters at both indices are the same (and thus a palindrome)
Interleaving String Given strings s1, s2, and s3, find whether s3 is formed by an interleaving of s1 and s2. An interleaving of two strings s and t is a configuration where s and t are divided into n and m substrings respectively, such that: s = s1 + s2 + ... + sn t = t1 + t2 + ... + tm |n - m| <= 1 The interleaving is s1 + t1 + s2 + t2 + s3 + t3 + ... or t1 + s1 + t2 + s2 + t3 + s3 + ... Note: a + b is the concatenation of strings a and b. Example 1: Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" Output: true Explanation: One way to obtain s3 is: Split s1 into s1 = "aa" + "bc" + "c", and s2 into s2 = "dbbc" + "a". Interleaving the two splits, we get "aa" + "dbbc" + "bc" + "a" + "c" = "aadbbcbcac". Since s3 can be obtained by interleaving s1 and s2, we return true.
USE 2D DP We can cache the recursive version and even convert that into an iterative version. Recursively, we know that we have two options, use s1 or s2 and if that substring works, so does this one. BF -> memo[i][j] = memo[i+1][j], if s1[i] == s3[i + j] OR memo[i][j+1], if s2[j] == s3[i + j] Base case is when both strings are exhausted, i == n and j == m where we can return true. If either string runs out prematurely, just return whether the remaining strings match Iteratively, we can work this problem bottom up also. Start with the same 2D memo array but work backwards through each dimension (n+1, m+1), check if we're in bound and assign memo[i][j] accordingly to BF above. Return memo[0][0]
Longest Common Subsequence Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0. A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters. For example, "ace" is a subsequence of "abcde". A common subsequence of two strings is a subsequence that is common to both strings. Example 1: Input: text1 = "abcde", text2 = "ace" Output: 3 Explanation: The longest common subsequence is "ace" and its length is 3. Example 2: Input: text1 = "abc", text2 = "abc" Output: 3 Explanation: The longest common subsequence is "abc" and its length is 3.
USE 2D DP m+1 rows (last row represents end char) and n+1 cols where m = len(text1) and n = len(text2). Now each memo[i][j] = the LCS from text1[i:] and text2[j:]. There are 3 possible subproblems: 1. the characters match so we can increment i+1 and j+1 (2. the characters don't match so we check i+1 AND 3. the characters don't match so we check j+1) dp[i][j] = - 1 + dp[i+1][j+1], if chars match - max( dp[i+1][j], dp[i][j+1] ) return dp[0][0]
Target Sum You are given an integer array nums and an integer target. You want to build an expression out of nums by adding one of the symbols '+' and '-' before each integer in nums and then concatenate all the integers. For example, if nums = [2, 1], you can add a '+' before 2 and a '-' before 1 and concatenate them to build the expression "+2-1". Return the number of different expressions that you can build, which evaluates to target. Example 1: Input: nums = [1,1,1,1,1], target = 3 Output: 5 Explanation: There are 5 ways to assign symbols to make the sum of nums be target 3. -1 + 1 + 1 + 1 + 1 = 3 +1 - 1 + 1 + 1 + 1 = 3 +1 + 1 - 1 + 1 + 1 = 3 +1 + 1 + 1 - 1 + 1 = 3 +1 + 1 + 1 + 1 - 1 = 3
USE 2D DP (0/1 KNAPSACK) In this case, we can either use a number positively or negatively to reach a target sum. Recursively, we know our base case is when i is out of bounds and the running sum is the target. In this case we return 1. If we're just out of bounds without the sum, return 0. We can dp in 2 ways: 1. use number positively and increment index or 2. use number negatively and increment index At many levels, we'll see the same running sum, at which point all the following paths will be the same. Therefore, we can introduce a 2D cache for each (i, sum) pair that holds the # of ways to reach target from here. return cache(0,0) which holds all ways
Coin Change II You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money. Return the number of combinations that make up that amount. If that amount of money cannot be made up by any combination of the coins, return 0. You may assume that you have an infinite number of each kind of coin. The answer is guaranteed to fit into a signed 32-bit integer. Example 1: Input: amount = 5, coins = [1,2,5] Output: 4 Explanation: there are four ways to make up the amount: 5=5 5=2+2+1 5=2+1+1+1 5=1+1+1+1+1
USE 2D DP (UNBOUND KNAPSACK) Trying to get to target amount, with unlimited coins. That means we can either use this coin, or skip this coin as many times as we want. Bottom up means we can start at 0 and sum coins till we reach amount. our BF -> dp[i][a] = dp[i+1][a], since we've always filled out the row below + dp[i][ a - coin[i] ], if that's within bounds since we know we've filled out everything less than a ^ for each amount, try each coin (in reverse order to avoid repeating paths) USE 1D DP We can do this in less space complexity if we fill out each coin row first rather than filling out each amount column. This way we only need to keep track of the previous coins row.
Unique Paths 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. Example 1: Input: m = 3, n = 7 Output: 28
USE 2D DP! We want to keep track of the number of paths to get to some point (i, j). Point (i, j) can be reached by going down from (i-1, j) or going right from (i, j - 1). Therefore we can get to (i, j) in the ways to get to the left or above it. BF equation dp[i][j] = dp[i - 1][j] + dp[i][j - 1] dp[0][0] is set to 1 at the start and our answer is ofc at dp[m-1][n-1]
Number of Weak Characters You are given a 2D integer array properties where properties[i] = [attacki, defensei] represents the properties of the ith character in the game. A character i is said to be weak if there exists another character j where attackj > attacki and defensej > defensei. Return the number of weak characters. Example 1: Input: properties = [[5,5],[6,3],[3,6]] Output: 0 Explanation: No character has strictly greater attack and defense than the other.
USE A SORT sort properties by attack in descending order, and ascending order for defense when they have the same attack for ex: [[6, 4] [6,6] [5,3] [3,2] [3,4]] keep track of the max defense seen so far for each sorted value - if value[i][1] < max_defense, then you have a smaller attack and defense than the previously seen max, so increment num weak chars - update max_def accordingly
Basic Calculator II Given a string s which represents an expression, evaluate this expression and return its value. The integer division should truncate toward zero. You may assume that the given expression is always valid. All intermediate results will be in the range of [-231, 231 - 1]. Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as eval(). Example 1: Input: s = "3+2*2" Output: 7
USE A STACK init a stack, a sign variable (as '+' to represent a positive), and a num value as 0 for each character in the string - if the current character is a digit, set num = num * 10 + character - otherwise -- if the last seen sign is +, stack.push(num) -- if the last seen sign is -, stack.push(-num) -- if the last seen sign is *, stack.push(stack.pop() * num) ---> you're multiplying the last seen number (with correct pos/neg) -- if the last seen sign is /, stack.push(stack.pop()/num) -- sign = character, and reset num to 0 now add up everything in the stack in order and return the result
Permutations Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. Example 1: Input: nums = [1,2,3] Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
USE BACKTRACKING - Keep a list of numbers visited (l) and a set of numbers visited (s) Recursion : - base case, if size(l) = size(nums) add l to answer and return - iterate through all numbers not in s, add them to l&s and backtrack (remove from l&s when done) return list of answers
Combination Sum Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order. The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different. The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input. Example 1: Input: candidates = [2,3,6,7], target = 7 Output: [[2,2,3],[7]] Explanation: 2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times. 7 is a candidate, and 7 = 7. These are the only two combinations.
USE BACKTRACKING Basic setup: - list of lists that add up to target - sort nums array backtrack: - if remain < 0, return (base case when you've overshot the target) - if remain == 0, add the current list of nums to answer list (base case when sum of current list == target) and return otherwise, - iterate from current index in nums to end - add nums[new_idx] to current list of nums and backtrack - remove the last entry in current list and repeat return list of lists
N Queens The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other. Given an integer n, return all distinct solutions to the n-queens puzzle. You may return the answer in any order. Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space, respectively. Example 1: Input: n = 4 Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]] Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above
USE BACKTRACKING Each queen makes its row, column, diagonal left, diagonal right used. Since we will dfs only in the down direction, we only need to worry about everything BELOW the current queen. To keep track of column, diagonal left, diagonal right, we'll need a set for each determining what column we just placed a queen in. This is because we can rule out diagonal and anti-diagonal lines with slopes of 1 or -1. we can check if a diagonal has been used by checking (r-c) in diagonals because that falls on a unique line (where z = y - x) Similarly, anti-diagonals follow (r + c) and can be checked as well because they fall on unique lines (where z = y + x) [0,-1,-2,-3] [0, 1, 2, 3] [1, 0 ,-1,-2] [1, 2, 3, 4] [2, 1, 0, -1] [2, 3, 4, 5] [3, 2, 1, 0] [3, 4, 5, 6] diagonal anti-diag Backtrack - if row == n, return the full board iterate through columns in n: - if c in cols, r-c in diagonals, or r+c in anti-diagonal: skip column - backtrack and add/remove to sets & board
Subsets Given an integer array nums of unique elements, return all possible subsets (the power set). The solution set must not contain duplicate subsets. Return the solution in any order. Example 1: Input: nums = [1,2,3] Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
USE BACKTRACKING keep a list of subsets BFS backtrack (call with subset=[] and start=0) - add current subset to subsets - from start idx to end - > add num[idx] to curr - > recurs backtrack with curr subset and start + 1 - > remove num[idx] You have added subsets as you are building them DFS backtrack (call with subset=[] and idx=0) - base case is when idx > size(nums) - two options: add nums[idx] or don't add nums[idx] - option 1) add nums[idx] to current subset and recurs backtrack with start + 1 - option 2) remove nums[idx] and recurs backtrack with start + 1
Word Search Given an m x n grid of characters board and a string word, return true if word exists in the grid. The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once. Example 1: Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED" Output: true
USE BACKTRACKING keep track of current (i, j), all visited idxs, and the current index of word (k) backtrack - base case: k == size(word), return True - bad case: (i, j) is out of bounds or the current char of board isn't the same as word[k] - otherwise, backtrack in all four directions (or reduce cases by checking) and pass k+1 Because the word can start at any index, iterate through all of board and backtrack separately. Return true if any of the cases passed
Subsets II Given an integer array nums that may contain duplicates, return all possible subsets (the power set). The solution set must not contain duplicate subsets. Return the solution in any order. Example 1: Input: nums = [1,2,2] Output: [[],[1],[1,2],[1,2,2],[2],[2,2]]
USE BACKTRACKING sort input array s.t. all like values are next to each other. We will backtrack like normal, adding backtracking and removing the current number in level order, but we will skip any subsequent repeats in input array. backtrack (curr_subsets = [], idx =0) - base case: idx is larger than size(nums) - always add curr_subset to answer - iterate through all values from idx and if i = idx or this number isn't a repeat (same as last) - > add num to subset, backtrack, remove num return all subsets found
Combination Sum II Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target. Each number in candidates may only be used once in the combination. Note: The solution set must not contain duplicate combinations. Example 1: Input: candidates = [10,1,2,7,6,1,5], target = 8 Output: [ [1,1,6], [1,2,5], [1,7], [2,6] ]
USE BACKTRACKING sort input array to allow skipping of used numbers backtrack (currSum, currList, start) - base case: if start exceeds size(nums) - extra case: add solution and return when currSum == target - backtrack on iteration (skip any repeats)
Word Ladder A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that: Every adjacent pair of words differs by a single letter. Every si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList. sk == endWord Given two words, beginWord and endWord, and a dictionary wordList, return the number of words in the shortest transformation sequence from beginWord to endWord, or 0 if no such sequence exists. Example 1: Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] Output: 5 Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is 5 words long.
USE BFS Creating the graph efficiently is the bottleneck here. Once the graph is decided, just bfs from one word to the next until we find the target word when we can return the length. If that word is never found, return 0. To create the graph, iterate through each word - for each letter in current word - > check if that letter can be replaced by a->z and if its found in the wordList, then that is an edge to be traversed by bfs BFS - if current word is target, then we just return the level we're at - otherwise we go through each edge and increment level by 1 if the word is never found, return 0
Graph Valid Tree Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree. Example 1: Input: n = 5 edges = [[0, 1], [0, 2], [0, 3], [1, 4]] Output: true
USE BFS We know a tree has n-1 edges so check that first - Pick random node to be root and add to queue. - while queue isn't empty, pop for curr and check if curr already visited, and if not add all children who haven't been visited to queue - decrement n every iter return True if n is now 0, otherwise we haven't visited all nodes so return False
Cheapest Flights Within K Stops There are n cities connected by some number of flights. You are given an array flights where flights[i] = [fromi, toi, pricei] indicates that there is a flight from city fromi to city toi with cost pricei. You are also given three integers src, dst, and k, return the cheapest price from src to dst with at most k stops. If there is no such route, return -1. Example 1: Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1 Output: 700 Explanation: The graph is shown above. The optimal path with at most 1 stop from city 0 to 3 is marked in red and has cost 100 + 600 = 700. Note that the path through cities [0,1,2,3] is cheaper but is invalid because it uses 2 stops.
USE BFS (psuedo bellman ford) We only want to go down k + 1 levels, so keep track of level globally (this updates every time we go through the queue). We want to keep track of the min cost to so far so init a list costs of size n with all costs = INF bfs - check size and pop size # of items out of queue - add each of their neighbors to the queue iff the price to reach it is less than the previously saved price at the end check the price to dst and return the proper result
Longest Repeating Character Replacement You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. Return the length of the longest substring containing the same letter you can get after performing the above operations. Example 1: Input: s = "ABAB", k = 2 Output: 4 Explanation: Replace the two 'A's with two 'B's or vice versa.
USE CHAR DICT AND SLIDING WINDOW outer loop iterates the right pointer set most used letter to Math.max(most used letter, ++frequency[currChar - 'A']) to find the most commonly used letter so far while loop with sliding window verification to verify sliding window: check if the number of elements in the subarray is still greater than the number of allowed replacements and the most freq letter (basically ur checking if every character in this subarray can be changed into 1 matching character with the allowed replacements) in this loop, we have to decrement freq for each of the letters we remove by incrementing the left pointer at the end of the loop we check if this new valid subarray is the longest one we've seen return the maxLength
Valid Anagram Given two strings s and t, return true if t is an anagram of s, and false otherwise. Input: s = "anagram", t = "nagaram" Output: true
USE COUNT ARRAY Create a count array of size 26 to keep track of how many times each letter in string s is used iterate thru t after populating the array and now decrement the count array for each letter used if each letter is used the same number of times in s as it is in t, then they are valid anagrams and every element of the count array should be 0 if there's some value that is not a 0, then t is not a valid anagram
Minimum Number of Steps to Make Two Strings Anagram You are given two strings of the same length s and t. In one step you can choose any character of t and replace it with another character. Return the minimum number of steps to make t an anagram of s. An Anagram of a string is a string that contains the same characters with a different (or the same) ordering. Example 1: Input: s = "bab", t = "aba" Output: 1 Explanation: Replace the first 'a' in t with b, t = "bba" which is anagram of s.
USE COUNTERS (HASHMAPS) Count char freqs of both s and t Subtract the counts of t from s. Now all keys with equal counts should be replaced by 0, any character missing from s will be negative and any character missing from t will be positive. return sum of all positive counts (basically how many letters of s need to be changed to become an anagram of t)
Sliding Window Maximum You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window. Example 1: Input: nums = [1,3,-1,-3,5,3,6,7], k = 3 Output: [3,3,5,5,6,7]
USE DEQUE keep a result array, and keep track of its current idx keep a window deque of idxs that could have a max -> will be sorted by idx AND value [from greatest to least] for each value of nums array - while deque is not empty and the first item in the deque is not in i - k + 1 of i, use window.poll() to remove it as it is no longer an idx included in the window - while deque is not empty and the LAST item (like a stack) is less than nums[i], use window.pollLast() to remove it as it can't be the next potential max - add i to window - if i - k + 1 > 0 (first window is populated), result[i -k+1] = nums[window.peek()] (use first seen, largest value in answer) return result
Design Twitter Design a simplified version of Twitter where users can post tweets, follow/unfollow another user, and is able to see the 10 most recent tweets in the user's news feed. Implement the Twitter class: Twitter() Initializes your twitter object. void postTweet(int userId, int tweetId) Composes a new tweet with ID tweetId by the user userId. Each call to this function will be made with a unique tweetId. List<Integer> getNewsFeed(int userId) Retrieves the 10 most recent tweet IDs in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user themself. Tweets must be ordered from most recent to least recent. void follow(int followerId, int followeeId) The user with ID followerId started following the user with ID followeeId. void unfollow(int followerId, int followeeId) The user with ID followerId started unfollowing the user with ID followeeId.
USE DEQUE AND PRIORITY QUEUE Create User class with tweets(deque) and following(set) init - dictionary of users - universal time var post - add post to user's posts - handle default cases getNewsFeed - either merge all deques from following list OR push and pop all tweets from following list follow - add following to user's following - handle default cases remove - remove following to user's following - handle default cases
Max Area of Island 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. The area of an island is the number of cells with a value 1 in the island. Return the maximum area of an island in grid. If there is no island, return 0. Example 1: Input: grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]] Output: 6 Explanation: The answer is not 11, because the island must be connected 4-directionally.
USE DFS DFS: - set this node as visited (grid[i][j] = 0 or wtv flag number) - return 1 + area in all 4 directions (only go down valid paths) try starting from each position (i, j) and update max_area if this island has a greater area
Reconstruct Itinerary You are given a list of airline tickets where tickets[i] = [fromi, toi] represent the departure and the arrival airports of one flight. Reconstruct the itinerary in order and return it. All of the tickets belong to a man who departs from "JFK", thus, the itinerary must begin with "JFK". If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read as a single string. For example, the itinerary ["JFK", "LGA"] has a smaller lexical order than ["JFK", "LGB"]. You may assume all tickets form at least one valid itinerary. You must use all the tickets once and only once. Example 1: Input: tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]] Output: ["JFK","MUC","LHR","SFO","SJC"]
USE DFS Technically we could backtrack and just optimize by going in sorted order and adding to result after all edges have been traversed but that leads to O(V+E)^2 time complexity. Instead we want to DFS and do post order appending to result. - sort tickets and create graph - dfs - > go to all destinations from this airport in alphabetical order - > once done visiting, add this airport to front of result (to avoid reverse order) By the end, we've got a list in order that represents the itinerary
Surrounded Regions You are given an m x n matrix board containing letters 'X' and 'O', capture regions that are surrounded: Connect: A cell is connected to adjacent cells horizontally or vertically. Region: To form a region connect every 'O' cell. Surround: The region is surrounded with 'X' cells if you can connect the region with 'X' cells and none of the region cells are on the edge of the board. A surrounded region is captured by replacing all 'O's with 'X's in the input matrix board. Example 1: Input: board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]] Output: [["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
USE DFS We'll work backwards to decide what spaces are SAFE rather than what spaces are surrounded. DFS from all edges that are O's and mark their connections as S for safe Now iterate through board and for any S, convert back to O but for any O's leftover from dfsing, mark those as X's
Clone Graph Given a reference of a node in a connected undirected graph. Return a deep copy (clone) of the graph. Each node in the graph contains a value (int) and a list (List[Node]) of its neighbors. class Node { public int val; public List<Node> neighbors; } Example 1: Input: adjList = [[2,4],[1,3],[2,4],[1,3]] Output: [[2,4],[1,3],[2,4],[1,3]] Explanation: There are 4 nodes in the graph. 1st node (val = 1)'s neighbors are 2nd node (val = 2) and 4th node (val = 4). 2nd node (val = 2)'s neighbors are 1st node (val = 1) and 3rd node (val = 3). 3rd node (val = 3)'s neighbors are 2nd node (val = 2) and 4th node (val = 4). 4th node (val = 4)'s neighbors are 1st node (val = 1) and 3rd node (val = 3).
USE DFS dfs: - if curr node has been visited (check if value exists and return the cloned node), return it - otherwise create new cloned node with value - iterate through original neighbors and add the dfs(with that node) to the cloned node's neighbors return the root after calling dfs(original root)
Binary Tree Maximum Path Sum A path in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence at most once. Note that the path does not need to pass through the root. The path sum of a path is the sum of the node's values in the path. Given the root of a binary tree, return the maximum path sum of any non-empty path. Example 1: Input: root = [1,2,3] Output: 6 Explanation: The optimal path is 2 -
USE DFS set a global max variable for ease DFS into sub trees to find largest path so far Options: 1. curr.val 2. pathSum(curr.left) + curr.val 3. pathSum(curr.right) + curr.val 4. left_sum + right_sum + curr.val Base case: curr node is null, return 0 the global max sum can be calculated at each node (which is the case that the current node is basically the end of our path) which is max(global max, all options) to further recurse, however, we return the value that is max(option 1, option 2, or option 3) as we can pick ONLY 1 out of the two possible subtrees to follow as a path
Course Schedule There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai. For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1. Return true if you can finish all courses. Otherwise, return false. Example 1: Input: numCourses = 2, prerequisites = [[1,0]] Output: true Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.
USE DFS to look for a cycle. Keep track of currently visiting and visited spots. hasCycle(curr): - if curr is not a prereq for anything, set to visited and return False - otherwise go thru all unvisited children of curr, if its currently being visited, you have a cycle - > otherwise mark this child as currently visiting and check if it has a cycle if all goes well return False for no cycle and mark as visited iterate thru all unvisited courses and check if they have a cycle - if any do, return False bc the schedule cannot exist
Longest Increasing Path in a Matrix Given an m x n integers matrix, return the length of the longest increasing path in matrix. From each cell, you can either move in four directions: left, right, up, or down. You may not move diagonally or move outside the boundary (i.e., wrap-around is not allowed). Example 1: Input: matrix = [[9,9,4],[6,6,8],[2,1,1]] Output: 4 Explanation: The longest increasing path is [1, 2, 6, 9].
USE DFS & DP DFS at each point to find the longest path from that starting point. DP is a 2D array with each spot having the longest path from that specific spot (min is 1). DP[i,j] = 1 + max(dp[all other directions]). Return max of dp (or of all dfs calls)
The Maze There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up, down, left or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction. Given the ball's start position, the destination and the maze, determine whether the ball could stop at the destination. The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the
USE DFS (as a graph) create a visited array each recursion call will be to a new position create an array of +- 1 to move in up,down,left,right directions keep a list of possible new positions by going in each direction for each one of these directions: - use position parameter to get curr_position - while canKeepRolling -- increment a new x and y by adding the 2 parts of the direction array -- if new pos x and y are within bounds, unvisited, and the space isn't "1", update curr_position -- else set canKeepRolling to false - if curr_position is the target position, return true - add curr_pos to list of possible new positions add original_position to visited array for each of the new positions in list - if recursion on new position, return True return false
Number of Islands Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water. Example 1: Input: grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] Output: 1
USE DFS OF GRAPH Create a visited 2d array and have all values as false (in python for an MxN array, use [[False]*n for _ in range(m)] to init array ORRR for O(1) space just set grid[i][j] to # after it's visited!) Go through all values of input grid and dfs when you reach an unvisited '1' increment count of islands each time to dfs - mark current node as visited - visited all up, down, left, right neighbors that are also unvisited and '1'
Pacific Atlantic Water Flow There is an m x n rectangular island that borders both the Pacific Ocean and Atlantic Ocean. The Pacific Ocean touches the island's left and top edges, and the Atlantic Ocean touches the island's right and bottom edges. You are given an m x n integer matrix heights where heights[r][c] represents the height above sea level of the cell at coordinate (r, c). The island receives a lot of rain, and the rain water can flow to neighboring cells directly adjacent if the neighboring cell's height is less than or equal to the current cell's height. Return a 2D list of grid coordinates result where result[i] = [ri, ci] denotes that rain water can flow from cell (ri, ci) to both the Pacific and Atlantic oceans. Example 1: Input: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]] Output: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
USE DFS and SETS We want to make 2 sets, one full of cells that can reach the pacific and another full of cells that can reach the atlantic. To do this, work BACKWARDS (aka start at edges and find cells that can feed into this one) dfs: - mark pos as visited (in the respective pac or atl set) - go to all unvisited adjacent positions that are within bounds and are taller than current height now pacific and atlantic are explored by valid positions and we can return their intersection
Binary Tree Maximum Path Sum A path in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence at most once. Note that the path does not need to pass through the root. The path sum of a path is the sum of the node's values in the path. Given the root of a binary tree, return the maximum path sum of any non-empty path. Example: Input: root = [1,2,3] Output: 6 Explanation: The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6.
USE DFS w/ GLOBAL MAX The main idea behind this is that your current max can either be the left max + curr.val, right max + curr.val, or just curr.val (either you go left path, right path, or terminate at current node); but the global max can be any of those options (a.k.a wtv your current max is) or the combination of left + right + curr.val (the option that nothing above the current node should be included in the max path)
Path Sum III Given the root of a binary tree and an integer targetSum, return the number of paths where the sum of the values along the path equals targetSum. The path does not need to start or end at the root or a leaf, but it must go downwards (i.e., traveling only from parent nodes to child nodes). Example 2: Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 Output: 3
USE DFS w/ MEMOIZATION Kind of like subarray sum equals k where you want to figure out prefixes and uses a dictionary to keep track of what prefixes have been seen/exist. if curr_sum - k has been seen as a prefix before, add it to the result. While DFSing, calculate current running sum, add the counts of the prefix being found to result (which could be 0 p = curr_sum - k). Add one to the counts of this current sum. DFS to right and left. Remove one from the counts of this current sum. You add first before dfsing to count it as part of the sum, but when you return to a higher level, you need to remove this current sum as the node is no longer 'visited'.
Swim in Rising Water You are given an n x n integer matrix grid where each value grid[i][j] represents the elevation at that point (i, j). The rain starts to fall. At time t, the depth of the water everywhere is t. You can swim from a square to another 4-directionally adjacent square if and only if the elevation of both squares individually are at most t. You can swim infinite distances in zero time. Of course, you must stay within the boundaries of the grid during your swim. Return the least time until you can reach the bottom right square (n - 1, n - 1) if you start at the top left square (0, 0). Example 1: Input: grid = [[0,2],[1,3]] Output: 3 Explanation: Max of path is 3
USE DIJKSTRA'S Use a visited set (storing tuple(i, j)) and a priority queue (cost, i, j). By using a priority queue means we don't have to worry about rewriting a lower score over the current node. Update queue in a direction that is unvisited. At each iteration of popping the pq, update the max cost by using max(grid[i][j], max_cost). When i == j == N, return max cost
Network Delay Time You are given a network of n nodes, labeled from 1 to n. You are also given times, a list of travel times as directed edges times[i] = (ui, vi, wi), where ui is the source node, vi is the target node, and wi is the time it takes for a signal to travel from source to target. We will send a signal from a given node k. Return the minimum time it takes for all the n nodes to receive the signal. If it is impossible for all the n nodes to receive the signal, return -1. Example 1: Input: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2 Output: 2
USE DIJKSTRA'S (BFS + MINHEAP) Basically creating an MST meaning we'll need an adj list with weights. Then keep min heap of (weight, next_node) which we pop to get current node. At each step, we check if curr is visited, or if visited.size == n, or add all unvisited children to minHeap as (time + weight, next) if by the end of our traversal we haven't returned, return -1 because there are some nodes that can't be reached
Search in Rotated Sorted Array There is an integer array nums sorted in ascending order (with distinct values). Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. Given the array nums after the possible rotation and an integer
USE DIVIDE AND CONQUER array nums is of length n+1 First use dc to find the smallest value: - start i,j at 0 and n and go until i >= j. - update i to be mid + 1 when nums[mid] > nums[j] -- (you know the smallest value must be on the right half of the -- array) - update j to be mid when nums[mid] <= nums[j] rotated to the right = i reset i and j to be 0 and n again - while i <= j - get a true_mid value by doing (mid+rotated)%n - if nums[true_mid] == target then you're done return true_mid - if nums[true_mid] > target, then i = mid + 1 - else, j = mid - 1 if nothing is found by the end, return -1
LRU Cache Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. Implement the LRUCache class: LRUCache(int capacity) Initialize the LRU cache with positive size capacity. int get(int key) Return the value of the key if the key exists, otherwise return -1. void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least re
USE DOUBLY LINKEDLIST AND HASHMAP Map: key-> cache_key, value-> Node(cache_key, cache_value) Dummy_head = new Node(-1,-1) Dummy_tail = new Node(-1,-1) global capacity constructor: - dummy_head.next = dummy_tail - dummy_tail.prev = dummy_head add_node_to_start: - new_start.next = old_start - new_start.prev = dummy_head - dummy_head.next = new_start - old_start.prev = new_start delete_node: - prev_copy = delete_this.prev - next_copy = delete_this.next - prev_copy.next = next_copy - next_copy.prev = prev_copy get: - if key isn't in map, return -1 - save node and value from map - delete node.key in map - delete node in DLL - add new node(key, value) to start - put (key, dummy_head.next) in map put: - if key is in map, save node -- remove node.key from map -- remove node from DLL - if map.size() == capacity, -- remove dummy_tail.prev.key from map -- remove dummy_tail.prev from DLL - add new node to start - put (key, dummy_head.next) in map
House Robber II You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have a security system connected, and it will automatically contact the police if two adjacent houses were broken into on the same night. Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police. Example 1: Input: nums = [2,3,2] Output: 3 Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2), because they are adjacent houses.
USE DP Basically house robber but do it twice. We want to run the algorithm once with the first house and one with the last house. BF equation -> house[i] = max(house[i-1], house[i-2] + nums[i]) After we keep both results, return the max
Partition Equal Subset Sum Given an integer array nums, return true if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or false otherwise. Example 1: Input: nums = [1,5,11,5] Output: true Explanation: The array can be partitioned as [1, 5, 5] and [11]. Example 2: Input: nums = [1,2,3,5] Output: false Explanation: The array cannot be partitioned into equal sum subsets.
USE DP Because of the restrictions, it isn't enough just to bfs because there are 2^n different combos you can make. We need a different way to see if we can get to the sum(nums)/2. We can keep track of all the subarray sums we can make up until the current value. Our memo in this case is the SET OF ALL SUMS we can make. The intuition behind this is because we're not trying to find longest or least, or most of some value but rather just whether or not we can reach a certain value. Therefore, index is somewhat unnecessary. Iterate through nums backwards - iterate through all subsums so far - > check if that sum is == target - > add the current number to each sum and to the set of all subsums if we never find a subsum == target, return False
House Robber You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night. Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police. Example 1: Input: nums = [1,2,3,1] Output: 4 Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3). Total amount you can rob = 1 + 3 = 4.
USE DP In this problem, we want to keep track of the max money we can rob from non-adj houses, which means we can either rob the current house and the ones at least one house away or just rob the immediate houses to our sides. BF equation -> house[i] = max(house[i-1], house[i-2] + nums[i]) Iteratively, this means we can just keep track of the two house maxes before i (prev1 and prev2 for example) and apply the bf equation to update prev1. We can just return the last value of prev1.
Climbing Stairs You are climbing a staircase. It takes n steps to reach the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? Example 1: Input: n = 2 Output: 2 Explanation: There are two ways to climb to the top. 1. 1 step + 1 step 2. 2 steps Example 2: Input: n = 3 Output: 3 Explanation: There are three ways to climb to the top. 1. 1 step + 1 step + 1 step 2. 1 step + 2 steps 3. 2 steps + 1 step
USE DP Iterative bottom up approach, you can get to each step n in ways[n-1] + ways[n-2]. Here we can save ways[n-1] and ways[n-2] as num1 and num2 to avoid O(N) space. now ans = num1 + num2 where they are both init to 1 respectively. starting from 2 -> n update ans, num1 and num2 accordingly. return ans
Maximum Profit in Job Scheduling We have n jobs, where every job is scheduled to be done from startTime[i] to endTime[i], obtaining a profit of profit[i]. You're given the startTime, endTime and profit arrays, return the maximum profit you can take such that there are no two jobs in the subset with overlapping time range. If you choose a job that ends at time X you will be able to start another job that starts at time X. Example 1: Input: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70] Output: 120 Explanation: The subset chosen is the first and fourth job. Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70.
USE DP Keep a dp 2d array of size (n,2) where dp[i] = [endTime, maxProfit] and n is arbitrary sort all jobs by end times zipped such that job[i] = (start[i], end[i], profit[i]) iterate through each job (s, e, p): - find the largest index s.t. we start after dp[i]'s end time (use bisect(dp, [s+1]) to find where s + 1 can be inserted remebering [3] < [3,1]) - if dp[i]'s profit + p is greater than the current longest job's profti (dp[-1][1]), append dp[e, new_profit] return dp[-1][1] (last entry)
Count Square Submatrices with All Ones Given a m * n matrix of ones and zeros, return how many square submatrices have all ones. Example 1: Input: matrix = [ [0,1,1,1], [1,1,1,1], [0,1,1,1] ] Output: 15 Explanation: There are 10 squares of side 1. There are 4 squares of side 2. There is 1 square of side 3. Total number of squares = 10 + 4 + 1 = 15.
USE DP The goal is to find the largest size square of 1's and keep track of the squares you've seen so far. Bellman equation: dp[i][j] = 0 if the current value is a 0 or i == 0 or j == 0 otherwise, = 1 + min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) ^ checks if we're visiting a 1 and if we are, then checks if this is the corner to a bigger square for example: [1, 1] [1, 2] has a 2 in the bottom because the other 1s surrounding it mean it's actually part of a 2x2 and not just a 1x1 by itself! to find solution: loop through all i and js and on each iteration, update dp if visiting a 1. After updating dp[][], add to a counter of # of squares which you can return. at dp[m][n], biggest square seen so far.
Word Break Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words. Note that the same word in the dictionary may be reused multiple times in the segmentation. Example 1: Input: s = "leetcode", wordDict = ["leet","code"] Output: true Explanation: Return true because "leetcode" can be segmented as "leet code". Example 2: Input: s = "applepenapple", wordDict = ["apple","pen"] Output: true Explanation: Return true because "applepenapple" can be segmented as "apple pen apple". Note that you are allowed to reuse a dictionary word.
USE DP We can call each subproblem a partition where everything after dp[i] is either a good partition or not. dp[n] = True by default because if we're at the end of our string, there's nothing left to partition from there we work backgrounds checking if s[i : i+word] == word to see if it's in the dict. Then we can set dp[i] = dp[i+word] because this partition is only valid if the partitions before it are also valid. We skip from i to i + word length bc this current slice is already well formed by the end, we can return dp[0] because it holds whether or not the whole string can be properly partitioned
Longest Increasing Subsequence Given an integer array nums, return the length of the longest strictly increasing subsequence. Example 1: Input: nums = [10,9,2,5,3,7,101,18] Output: 4 Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
USE DP We want to keep track of the LIS at each position, so iteratively this means checking for the LIS including what we're at now. BF equation -> dp[i] = max( - 1, if there aren't any nums smaller than nums[i] - dp[j] + 1, if nums[j] < nums[i] ) In practice, we'll update dp[i] as many as i times (for every num before it) which means we also want to include dp[i] in the max calculation We can then return the max value in the entire array (either keep track, or just check all values at final step)
Decode Ways You have intercepted a secret message encoded as a string of numbers. The message is decoded via the following mapping: "1" -> 'A'"2" -> 'B'..."25" -> 'Y'"26" -> 'Z' However, while decoding the message, you realize that there are many different ways you can decode the message because some codes are contained in other codes ("2" and "5" vs "25"). For example, "11106" can be decoded into: "AAJF" with the grouping (1, 1, 10, 6) "KJF" with the grouping (11, 10, 6) The grouping (1, 11, 06) is invalid because "06" is not a valid code (only "6" is valid). Given a string s containing only digits, return the number of ways to decode it. If the entire string cannot be decoded in any valid way, return 0. The test cases are generated so that the answer fits in a 32-bit integer. Example 1: Input: s = "12" Output: 2 Explanation: "12" could be decoded as "AB" (1 2) or "L" (12).
USE DP We want to keep track of the number of ways to decode substring s[:i+1] so we'll store the previous answers in an array (or just 2 variables for reduced space complexity!) We want to see if the current number can be decoded on its own, in which case our total amt of decodes is at least what it was for s[:i]. We also want to see if the current number with the number before it (s[i-1]) can also be decoded, in which case our total amt of decodes is at least what it was for s[:i-1] BF equation is conditional -> ways[i] = + if s[i] is decodable, ways[i-1] + if s[i-1] + s[i] is decodable, ways[i-2] where ways[0] and ways[1] = 1 we can return ways[n] after iteratively building out ways
Coin Change You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money. Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1. You may assume that you have an infinite number of each kind of coin. Example 1: Input: coins = [1,2,5], amount = 11 Output: 3 Explanation: 11 = 5 + 5 + 1 Example 2: Input: coins = [2], amount = 3 Output: -1
USE DP We want to keep track of the smallest number of coins needed to reach each i <= amount From 1 -> amount - for each coin, pick the least value out of memo[amount - coin] and add one (to represent using the current coin) BF equation -> memo[i] = min(memo[i-j] + 1) where j is all coins return memo[amount] if possible
Min Cost Climbing Stairs You are given an integer array cost where cost[i] is the cost of ith step on a staircase. Once you pay the cost, you can either climb one or two steps. You can either start from the step with index 0, or the step with index 1. Return the minimum cost to reach the top of the floor. Example 1: Input: cost = [10,15,20] Output: 15 Explanation: You will start at index 1. - Pay 15 and climb two steps to reach the top. The total cost is 15. Example 2: Input: cost = [1,100,1,1,1,100,1,1,100,1] Output: 6 Explanation: You will start at index 0. - Pay 1 and climb two steps to reach index 2. - Pay 1 and climb two steps to reach index 4. - Pay 1 and climb two steps to reach index 6. - Pay 1 and climb one step to reach index 7. - Pay 1 and climb two steps to reach index 9. - Pay 1 and climb one step to reach the top. The total cost is 6.
USE DP iterative bottom up to avoid O(N) space complexity. the cost of the stair i is the minimum of the cost 1 step and 2 steps before it. BF equation -> mins[i] = cost[i] + min( mins[i-1], mins[i-2] ) Since we can get to the last step in 1 or 2 steps, return min( mins[n], mins[n-1] )
Maximum Product Subarray Given an integer array nums, find a subarray that has the largest product, and returnthe product. The test cases are generated so that the answer will fit in a 32-bit integer. Example 1: Input: nums = [2,3,-2,4] Output: 6 Explanation: [2,3] has the largest product 6.
USE DP (but really running max and min) By keeping track of max and min, we can take care of negative values swapping the sign of the current product. If we encounter a negative number, our min will become our new max and vice versa. For each number in nums, recacluate max and min between the following values - n * max, to see if the signs flip or if we go further positive - n * min, to see if the signs flip or if we go further negative - n, in case we need to restart return wtv our result is
Longest Palindromic Substring Given a string s, return the longest palindromic substring in s. Example 1: Input: s = "babad" Output: "bab" Explanation: "aba" is also a valid answer. Example 2: Input: s = "cbbd" Output: "bb"
USE DP (sorta) If we build out palindromes assuming the letter @ i is the middle, we can basically confirm that anything we add to it must be a palindrome as well. start at each letter: - left and right pointers are at i - > while the letter at l and r are equal, update our result if possible - now start w right at i + 1 and do the same loop as above (to check for even length palindromes) return wtv the result is at the end
Best Time to Buy and Sell Stock with Cooldown You are given an array prices where prices[i] is the price of a given stock on the ith day. Find the maximum profit you can achieve. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times) with the following restrictions: After you sell your stock, you cannot buy stock on the next day (i.e., cooldown one day). Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again). Example 1: Input: prices = [1,2,3,0,2] Output: 3 Explanation: transactions = [buy, sell, cooldown, buy, sell]
USE DP and STATES Our decisions in this case are a bit interesting as we have 3 options in total but only a few at any given time. If we are in the buy state, we can only buy or skip the day. If we are in the sell state, we can only sell and skip or just skip. As a tree, we can say at level 1 we buy or cooldown. If we buy, swap the state and subtract price[i] but if we cooldown, keep the state the same and do nothing. This means our BF equation is conditional -> dp[i, buy] = max(dp[i+1, sell] - price[i], dp[i+1, buy]) dp[i, sell] = max(dp[i+2, buy] + price[i], dp[i+1, sell]) Since 0,buy is the only option for the root, we can just return dp[0, buy] Another way to look at this is via state machines. At each state we have certain options and we can update those options as we go. s0, s1, and s2. s0 can rest or buy to go to s1, s1 can rest or sell to go to s2, s2 can rest to go to s0. We define base cases for each state, and dp
Detect Cycle in LinkedList Given head, the head of a linked list, determine if the linked list has a cycle in it. There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. Internally, pos is used to denote the index of the node that tail's next pointer is connected to. Note that pos is not passed as a parameter. Return true if there is a cycle in the linked list. Otherwise, return false. (3,-4) -> 2 -> 0- > -4 -> (cycle back to 2) Example 1: Input: head = [3,2,0,-4], pos = 1 Output: true Explanation: There is a cycle in the linked list, where the tail connects to the 1st node (0-indexed).
USE FAST AND SLOW POINTER approach 1: hashset and check if you've visited the node (space complexity O(N) approach 2: replace the value of nodes visited to smth out of range (space complexity O(1) but changes values) *****approach 3: use a fast and slow pointer that eventually meets if there's a cycle set fast and slow to head loop while fast.next and fast.next.next are != null in loop, inc slow by 1 (slow = slow.next) and inc fast by 2 (fast = fast.next.next) if fast == slow at any point, return that there is a cycle at the end of the loop, the fast pointer has reached the end, so return no cycle
Best Time to Buy and Sell Stocks II You are given an integer array prices where prices[i] is the price of a given stock on the ith day. On each day, you may decide to buy and/or sell the stock. You can only hold at most one share of the stock at any time. However, you can buy it then immediately sell it on the same day. Find and return the maximum profit you can achieve. Example 1: Input: prices = [7,1,5,3,6,4] Output: 7 Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), prof
USE GREEDY Iterate through array len-1 - anytime i < i+1, add difference to profits return sum of all profits
Jump Game II You are given a 0-indexed array of integers nums of length n. You are initially positioned at nums[0]. Each element nums[i] represents the maximum length of a forward jump from index i. In other words, if you are at nums[i], you can jump to any nums[i + j] where: 0 <= j <= nums[i] and i + j < n Return the minimum number of jumps to reach nums[n - 1]. The test cases are generated such that you can reach nums[n - 1]. Example 1: Input: nums = [2,3,1,1,4] Output: 2 Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index.
USE GREEDY INSPIRED BY BFS Because a solution is always guaranteed, we can be greedy in which path we choose to explore via bfs. Instead of just visiting every node at each level, we pick the one with the furthest reach keep a left and right pointer where left is the node we are jumping from and right is max node we can jump to. While right isn't the last node visitable, pick the furthest reach (not jump) possible and increment jump count. Return jumps taken
Pairs of Songs With Total Durations Divisible by 60 You are given a list of songs where the ith song has a duration of time[i] seconds. Return the number of pairs of songs for which their total duration in seconds is divisible by 60. Formally, we want the number of indices i, j such that i < j with (time[i] + time[j]) % 60 == 0. Example 1: Input: time = [30,20,150,100,40] Output: 3 Explanation: Three pairs have a total duration divisible by 60: (time[0] = 30, time[2] = 150): total duration 180 (time[1] = 20, time[3] = 100): total duration 120 (time[1] = 20, time[4] = 40): total duration 60
USE HASHMAP Twosum but with frequency hashmap - keep hashmap (or fixed size array) of numbers and their %60 value - check if 60 - num%60 is in the hashmap and inc number of solutions by frequency of that key return total number of solutions
Minimum Window Substring Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring, return the empty string "". The testcases will be generated such that the answer is unique. A substring is a contiguous sequence of characters within the string. Example 1: Input: s = "ADOBECODEBANC", t = "ABC" Output: "BANC" Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string t.
USE HASHMAP AND SLIDING WINDOW populate the hashmap with characters in t and their frequency iterate through s while keeping track of how many of the letters in t we've found and which ones are left for loop iterates the right pointer while fixing the left if the charAt(r) is in the hashmap, decrement the freq in the map and increase number of letters found while the num of letters found == t.length() then we need to increment the left pointer until we've gotten rid of all the unnecessary chars but first, make sure to check if this window is the smallest valid window seen if any characters we remove happen to be in t, decrement found, increment that character's hashmap, and exit the while loop return the smallest valid window seen
Two Sum Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. You may assume that each input would have exactly one solution, and you may not use the same element twice. You can return the answer in any order. Example 1: Input: nums = [2,7,11,15], target = 9 Output: [0,1] Output: Because nums[0] + nums[1] == 9, we return [0, 1].
USE HASHMAPS Create a hashmap that maps the num visited with its index. Iterate through each value in the array while checking if the key = target - nums[i] is in the hashmap. if yes, return an array with map.get(key) and i else, put(nums[i], i)
Kth Largest Element in a Stream Design a class to find the kth largest element in a stream. Note that it is the kth largest element in the sorted order, not the kth distinct element. Implement KthLargest class: KthLargest(int k, int[] nums) Initializes the object with the integer k and the stream of integers nums. int add(int val) Appends the integer val to the stream and returns the element representing the kth largest element in the stream. Example 1: Input ["KthLargest", "add", "add", "add", "add", "add"] [[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]] Output [null, 4, 5, 5, 8, 8] Explanation KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]); kthLargest.add(3); // return 4 kthLargest.add(5); // return 5 kthLargest.add(10); // return 5 kthLargest.add(9); // return 8 kthLargest.add(4); // return 8
USE MIN HEAP Basic idea: an min heap with k elements means the kth largest element is always the head of the heap init - truncate the list to k elements - add rest of elements from stream with heappushpop to maintain only the k largest elements populate the heap - OR you can just heapify the whole thing and pop till size == k add - push the value to heap - remove smallest element if heap size is now greater than k - return head of heap
K Closest Points to Origin Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). Example 1: Input: points = [[1,3],[-2,2]], k = 1 Output: [[-2,2]] Explanation: The distance between (1, 3) and the origin is sqrt(10). The distance between (-2, 2) and the origin is sqrt(8). Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]].
USE MIN HEAP Iterate through points - for each point find distance - push distance and point to heap - pop from heap if heap is larger than k return points from heap of size k
Last Stone Weight You are given an array of integers stones where stones[i] is the weight of the ith stone. Play game, smash heaviest two stones x, y | x <= y The result of this smash is: If x == y, both stones are destroyed, and If x != y, the stone of weight x is destroyed, and the stone of weight y has new weight y - x. At the end of the game, there is at most one stone left. Return the weight of the last remaining stone. If there are no stones left, return 0. Example 1: Input: stones = [2,7,4,1,8,1] Output: 1 Explanation: We combine 7 and 8 to get 1 so the array converts to [2,4,1,1,1] then, we combine 2 and 4 to get 2 so the array converts to [2,1,1,1] then, we combine 2 and 1 to get 1 so the array converts to [1,1,1] then, we combine 1 and 1 to get 0 so the array converts to [1] then that's the value of the last stone.
USE MIN HEAP make heap with negative weights to turn min heap into max heap while there are more than 1 stones, smash two heaviest stones and push the result (unless their weights were equal in which case do nothing) return either head of the heap or 0 (if heap is empty)
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. Can you solve it without sorting? Example 1: Input: nums = [3,2,1,5,6,4], k = 2 Output: 5
USE MIN HEAP OR QUICK SELECT Min heap - heapify nums - pop until k left - return head of heap quick select (recursive) - randomly pick pivot from nums - separate nums into 3 buckets (more, equal, or less than pivot) - if k < size of more, kth largest is in more, recurs with nums=more and k = k - if k > size of more and equal, kth largest is in less, recurs with nums=less and k= k-more-equal - otherwise kth largest is in mid and all mid vals are equal so return mid[0]
Find Median from Data Stream The median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value, and the median is the mean of the two middle values. For example, for arr = [2,3,4], the median is 3. For example, for arr = [2,3], the median is (2 + 3) / 2 = 2.5. Implement the MedianFinder class Example 1: Input ["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"] [[], [1], [2], [], [3], []] Output [null, null, null, 1.5, null, 2.0] Explanation MedianFinder medianFinder = new MedianFinder(); medianFinder.addNum(1); // arr = [1] medianFinder.addNum(2); // arr = [1, 2] medianFinder.findMedian(); // return 1.5 (i.e., (1 + 2) / 2) medianFinder.addNum(3); // arr[1, 2, 3] medianFinder.findMedian(); // return 2.0
USE MINHEAP + MAXHEAP init - create minheap and maxheap (s.t. size(min) <= size(max) and |max| - |min| <= 1) - max will store the smaller half of the datastream and min will store the other half add number - if size(max) == size(min), so far we have an even number of entries in stream - > pushpop the min heap offering the new num - > store the popped min heap item in the max heap - else, we have an odd number of items and we need to balance - > pushpop the max heap offering the new num - > store the popped max heap item in the min heap find median - if size(min) == size(max), average out the heads - else, return the head of max
Walls and Gates You are given an m×n 2D grid initialized with these three possible values: -1 - A wall that can not be traversed. 0 - A Gate. INF - A land cell that can be traversed. Fill each land cell with the distance to its nearest treasure chest. If a land cell cannot reach a treasure chest than the value should remain INF. Assume the grid can only be traversed up, down, left, or right. Example 1: Input: [ [INF,-1,0,INF], [INF,INF,INF,-1], [INF,-1,INF,-1], [0,-1,INF,INF]], Output:[[3,-1,0,1], [2,2,1,-1], [1,-1,2,-1], [0,-1,3,4]]
USE MULTI-SOURCE BFS BFSing a graph with equal edge weights will give you the shortest path to each node. We can use this idea with multiple sources (gates) and traverse legal moves and increment steps each time we pop a new level. First add all moves from spaces with 0, then while queue isn't empty, traverse each level (for i in queue length) marking visited as we go. Return the map
Rotting Oranges You are given an m x n grid where each cell can have one of three values: 0 representing an empty cell, 1 representing a fresh orange, or 2 representing a rotten orange. Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten. Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1. Example 1: Input: grid = [[2,1,1],[1,1,0],[0,1,1]] Output: 4
USE MULTI-SOURCE BFS each rotten orange can infect adjacent fresh oranges. first find all rotten oranges (and how many fresh oranges there are) then bfs from rotten oranges, incrementing minutes at each level (decrement fresh count when an orange turns rotten) if fresh count is 0, then return minutes otherwise, there are fresh oranges left and return -1
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. Example 1: Input: nums = [1,2,3,4] Output: [24,12,8,6]
USE PREFIX AND SUFFIX ARRAYS build a prefix array by setting the value of pre[0] to 1 and then the following pre[i] = pre[i-1] * nums[i-1] (this sets the current value to the product of the elements before it and the previous element) to build the suffix array, do the same but set post[end] = 1 and the following post[i] = post[i+1] * nums[i+1] (this sets the current value to the product of the elements after it and the next value) the result array can be built by result[i] = pre[i] * post[i]
Subarray Sum Equals K 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. Example 1: Input: nums = [1,1,1], k = 2 Output: 2 Example 2: Input: nums = [1,2,3], k = 3 Output: 2
USE PREFIX SUMS Calculate prefix sums as per usual Keep a dictionary with key=prefix and value=times seen before When iterating through all prefixes, prefix - k determines what sum would be needed to reach k. If prefix - k is in the first dictionary, add dictionary[prefix - k] to total solutions found. Add this current prefix to the seen dictionary Return # of solutions
Subarray Sums Divisible by K Given an integer array nums and an integer k, return the number of non-empty subarrays that have a sum divisible by k. A subarray is a contiguous part of an array. Example 1: Input: nums = [4,5,0,-2,-3,1], k = 5 Output: 7 Explanation: There are 7 subarrays with a sum divisible by k = 5: [4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]
USE PREFIX SUMS This problem can be efficiently solved if you understand that if prefix sums s_i % k = s_j % k then the subarray sum from i->j % k = 0. In other words, we want to keep track of
Min Cost to Connect All Points You are given an array points representing integer coordinates of some points on a 2D-plane, where points[i] = [xi, yi]. The cost of connecting two points [xi, yi] and [xj, yj] is the manhattan distance between them: |xi - xj| + |yi - yj|, where |val| denotes the absolute value of val. Return the minimum cost to make all points connected. All points are connected if there is exactly one simple path between any two points. Example 1: Input: points = [[0,0],[2,2],[3,10],[5,2],[7,0]] Output: 20
USE PRIM'S (BFS with MinHeap) We are trying to create an MST, so first step is to find all the edges then BFS based on min distance but keep track of any nodes that have already been used KEY INFO: if you've added something to the queue, it may get added twice because it's not marked visited until AFTER it's been popped. So always have a check if the newly popped node is visited and make sure to not add visited nodes to queue
Merge k Sorted Lists You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. Merge all the linked-lists into one sorted linked-list and return it. Example 1: Input: lists = [[1,4,5],[1,3,4],[2,6]] Output: [1,1,2,3,4,4,5,6] Explanation: The linked-lists are: [ 1->4->5, 1->3->4, 2->6 ] merging them into one sorted list: 1->1->2->3->4->4->5->6
USE PRIORITY QUEUE (or divide and conquer for better space complexity) For each linked-list, populate a PQ with the nodes (use new PriorityQueue<>((a,b) -> a.val-b.val) as the lambda expression that compares the values of two nodes) If the PQ is empty here, just return null Save a head node as the first poll While PQ isn't empty-> Then poll each node in order and set it as the curr.next, update curr to curr.next If in this while PQ becomes empty, set curr.next to null
Task Scheduler You are given an array of CPU tasks, each represented by letters A to Z, and a cooling time, n. Each cycle or interval allows the completion of one task. Tasks can be completed in any order, but there's a constraint: identical tasks must be separated by at least n intervals due to cooling time. Return the minimum number of intervals required to complete all tasks. Example 1: Input: tasks = ["A","A","A","B","B","B"], n = 2 Output: 8 Explanation: A possible sequence is: A -> B -> idle -> A -> B -> idle -> A -> B. After completing task A, you must wait two cycles before doing A again. The same applies to task B. In the 3rd interval, neither A nor B can be done, so you idle. By the 4th cycle, you can do A again as 2 intervals have passed.
USE PRIORITY QUEUE OR FORMULA PQ - keep a pq of frequencies for each character (that way we can pop out the most frequent one) - keep track time elapsed - while pq isn't empty: build next cycle (do n + 1 tasks) - > pop the most frequent task and subtract by 1 - > add it to temp list if it's frequency is still not 0 - > update time by 1 and decrement cycle by 1 - add all temp list items back to queue - if queue is empty, break - otherwise, we need to idle for the leftover cycle (time += cycle) FORMULA - Each cycle will have all the most freq tasks and a space for filler until the cooldown time is over - calculate the highest frequency of task and set it to F - calculate the amount of tasks with frequency F and let that number be T - the number of cycles with required idle time is F - 1 - the length of each cycle is n + 1 - the length of the final cycle will only be T (no idle time necessary) - return either the length of tasks or (number of cycles) * (length of cycle) + T
Construct Binary Tree from Preorder and Inorder Traversal Given two integer arrays preorder and inorder where preorder is the preorder traversal of a binary tree and inorder is the inorder traversal of the same tree, construct and return the binary tree. Example 1: Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] Output: [3,9,20,null,null,15,7]
USE RECURSION Basic idea is to find the current 'top' of the tree and build the left and right sides. Keep preorder as a list of nodes we haven't built yet, and inorder will be the list of nodes we can put into our tree at the current root. Base case: inorder has no nodes, return None Each recur, we pop the top of preorder (like a queue) and assign it as root (by indexing inorder). Then assign left to the recur on left side (list[0:idx]) and right to the recur on right (list[idx+1:]). return root At the end, all left and rights of each node will have been filled including the root which is the final return in the stack. For more efficiency: - Create map for indices (no more linear search) - recurs (new local func) with slices of inorder with idxs i and k - base case is now k < i, return None - still pop like queue, and still idx left and right of curr node
Serialize and Deserialize Binary Tree Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment. Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure. Clarification: The input/output format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself. Example 1: Input: root = [1,2,3,null,null,4,5] Output: [1,2,3,null,null,4,5]
USE RECURSIVE PREORDER When serializing and deserializing, it's important to do both processes in the same order. Serialize - In this case, we serialize by adding the visited node to a list then visiting left and right after. - At the end, we join all entries with ',' as the delimiter - To take care of imperfect trees, add 'null' as children to leaf nodes Deserialize - Split the string using the delimiter ',' - Follow the same algorithm by decoding the current node then assign left child to deserialize(left) and right child to deserialize(right) - return the root
Most Profit Assigning Workers You have n jobs and m workers. You are given three arrays: difficulty, profit, and worker where: difficulty[i] and profit[i] are the difficulty and the profit of the ith job, and worker[j] is the ability of jth worker (i.e., the jth worker can only complete a job with difficulty at most worker[j]). Every worker can be assigned at most one job, but one job can be completed multiple times. For example, if three workers attempt the same job that pays $1, then the total profit will be $3. If a worker cannot complete any job, their profit is $0. Return the maximum profit we can achieve after assigning the workers to the jobs. Example 1: Input: difficulty = [2,4,6,8,10], profit = [10,20,30,40,50], worker = [4,5,6,7] Output: 100 Explanation: Workers are assigned jobs of difficulty [4,4,6,6] and they get a profit of [20,20,30,30] separately.
USE SORT & 2 POINTER Sort jobs ( a zip of difficulty and profit) and sort workers, both ascending. One pointer will keep track of worker (outer) and the other will keep track of most difficult job that worker could do (inner). Now any worker at j > i can do all the jobs i could do. Keep a running max of profit that each worker can do. Add that to total profit once worker cannot do any more difficult jobs. return total profit
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]]
USE SORT AND SKIP DUPLICATES AND 2 POINTER 1. create the answer list 2. sort the array so duplicates are next to each other and largest values are on the right while the smallest are on the left 3. iterate through the array while in bounds and while the values are negative (otherwise there's no sum that reaches 0) 4. if this is isn't the first iteration, and the value is a repeat of the last one, skip this case 5. otherwise, set j to i + 1 and k to length - 1 (j and k are two pointers) -> if the sum is 0, add this triplet to answer list then skip all repeat values of j and k -> else if the sum > 0, decrease sum by decrementing k (make k a smaller num) -> else sum < 0, increase sum by incrementing j (make j a bigger num) 6. return list of all triplets
Longest Valid Parentheses Given a string containing just the characters '(' and ')', return the length of the longest valid (well-formed) parentheses substring . Example 1: Input: s = "(()" Output: 2 Explanation: The longest valid parentheses substring is "()". Example 2: Input: s = ")()())" Output: 4 Explanation: The longest valid parentheses substring is "()()".
USE STACK populate stack with indices of parentheses keep track of longest length so far for each character in the string - if char is '(', push it to the stack - otherwise, pop -- if the stack is now empty, push this idx to the stack -- else, update longest by finding max between itself and idx - stack.pop (i.e. last potential start to a set of parentheses) return longest basically we update the stack with potential starts (i.e. '(' ) and then pop when we encounter a ')' until the only thing in the stack is the earliest start. if its empty then this is the new earliest start.
Valid Parentheses Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. An input string is valid if: Open brackets must be closed by the same type of brackets. Open brackets must be closed in the correct order. Example 1: Input: s = "()" Output: true
USE THE STACK! create a stack onto which you push the respective closing bracket (i.e. ']', ')', or '}') when encountering the open variant. otherwise, if the current character in the string s is not an open bracket, check if stack is empty or if the popped value is != to the current character (we've encountered the wrong closing bracket) if yes to either, then return false else at the end of the iterations, return true
Course Schedule II There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai. For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1. Return the ordering of courses you should take to finish all courses. If there are many valid answers, return any of them. If it is impossible to
USE TOPOLOGICAL SORT Create a adjList map (key: course #, value: list of all classes that require this course as a pre-req) Create a list of indegrees of size num_courses for pre-req in pre-reqs - increment in degree for course# pr[0] - add pr[0] to the adjList of pr[1] Create a queue of nodes with 0-indegree while queue isn't empty - pop queue for next course to add to order - for all courses that require the current one, -- remove 1 indegree, and add to queue if new indegree == 0 - count iterations if iterations in while loop < num_courses, return empty array [] otherwise return order visited.
Design Add and Search Words Data Structure Design a data structure that supports adding new words and finding if a string matches any previously added string. Implement the WordDictionary class: WordDictionary() Initializes the object. void addWord(word) Adds word to the data structure, it can be matched later. bool search(word) Returns true if there is any string in the data structure that matches word or false otherwise. word may contain dots '.' where dots can be matched with any letter. Example: Input ["WordDictionary","addWord","addWord","addWord","search","search","search","search"] [[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]] Output [null,null,null,null,false,true,true,true]
USE TRIE Implement trie to for add functions search(word): dfs to check all possible characters when at '.' position - base case, end of word reached, return if it's a word - if dealing with word[idx] = '.', dfs through all possible characters at this level in trie and return True if any of them succeed - if word[idx] is in the current level, recurs - otherwise return False
Implement Trie (Prefix Tree) A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker. Implement the Trie class: Trie() Initializes the trie object. void insert(String word) Inserts the string word into the trie. boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise. boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise. Example 1: Input ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] Output [null, null, true, false, true, null, true]
USE TRIE create a class for trie nodes with members map and isWord init: - set root to new Node insert (word): - iterate thru letters in word - > if letter isn't in the current node's map, create it - > iterate to next node for next letter in map - set current node (after all letters added) to isWord = True search(word): - iterate thru letters in word - > if letter isn't in map: return False - > keep iterating to next node - return current node's isWord value startsWith(prefix): - go through same logic as search but just return True if loop is finished
Word Search II Given an m x n board of characters and a list of strings words, return all words on the board. Each word must be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once in a word. Example 1: Input: board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"] Output: ["eat","oath"]
USE TRIE AND DFS Basic idea: Build a trie with all words in list, dfs from each (i,j) pos on board to find words, delete trie words when found dfs: (current_node, current_letter, i, j) - Because we want to delete entries when words have been found, keep track of a current_node which has reference to the child node (curr[current_letter]) - if child node is word, add word to result and delete that word - otherwise dfs from child node to any other valid position within the node's set of letters - if the child node is now empty, delete it In this case, remember ends of words with node['#'] = word
Container with Most Water Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of the line i is at (i, ai) and (i, 0). Find two lines, which, together with the x-axis forms a container, such that the container contains the most water. 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 represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.
USE TWO POINTER while leftPtr < rightPtr keep track of current water contained by doing curr = Math.min(height[l], height[r]) * (right - left) keep track of max water contained by doing max = Math.max(curr, max) we then want to change the pointer that is at the smaller height in hopes of finding something taller after loop, return max
Redundant Connection In this problem, a tree is an undirected graph that is connected and has no cycles. You are given a graph that started as a tree with n nodes labeled from 1 to n, with one additional edge added. The added edge has two different vertices chosen from 1 to n, and was not an edge that already existed. The graph is represented as an array edges of length n where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the graph. Return an edge that can be removed so that the resulting graph is a tree of n nodes. If there are multiple answers, return the answer that occurs last in the input. Example 1: Input: edges = [[1,2],[1,3],[2,3]] Output: [2,3]
USE UNION FIND Create find function to look for each node's parent (at start they are their own parent) Create union function to combine groups of nodes (make one parent's parent the other parent) If the edge that we are iterating over has nodes with the same parent, that is the redundant connection and return it
Validate Binary Search Tree Given the root of a binary tree, determine if it is a valid binary search tree (BST). A valid BST is defined as follows: The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys greater than the node's key. Both the left and right subtrees must also be binary search trees. Example 1: Input: root = [2,1,3] Output: true
USE UPPER AND LOWER BOUNDS We will use tail end recursion to solve this problem. We want to keep track of the lower and upper bounds of each node. When going left, we set the next node's upper value to the node we just visited and keep the lower bound as is. Likewise, while going right, we set the next node's upper bound to what it was previously but update the lower bound to the node we just visited. We then return false if a node falls outside those bounds and return true when we reach a null case;