Modified Copy LeetCode

¡Supera tus tareas y exámenes ahora con Quizwiz!

73. Set Matrix Zeroes Given an m x n matrix. If an element is 0, set its entire row and column to 0. Do it in-place. Follow up: A straight forward solution using O(mn) space is probably a bad idea. A simple improvement uses O(m + n) space, but still not the best solution. Could you devise a constant space solution? Constraints: m == matrix.length n == matrix[0].length 1 <= m, n <= 200 -231 <= matrix[i][j] <= 231 - 1

Insight/Solution: Array + index marking, this problem sucks (it's a good problem, just super tricky to deal with) you can use a set of rows and cols to handle which rows or columns need to be set to 0, but that will take O(m + n) space a constant space solution requires index marking, so here is the expression: if cell[i][j] == 0 { cell[i][0] = 0 cell[0][j] = 0 note: make sure to not zeroify matrix[0][0], since that is a special edge case that has to zeroify a row and a column, but there are cases when matrix[0][0] becomes 0 but row 0 does not get completely zeroified since any integer is fair game, we can only use 0 as a marker, but that presents plenty of problems if we stumble on a 0 that occurred when it shouldn't have, but notice that the 0s that we have to consider for when we decide to zeroify is all on the left and top border, so we can iterate inside that border first to zeroify now, check the borders, we know that the top border must be zeroified if matrix[0][0] == 0, and keep track of a boolean variable that lets us know if matrix[i][0] == 0, which would mean we have to zeroify the left border as well this problem is still pretty confusing, so review it again

48. Rotate Image You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise). You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation. Constraints: matrix.length == n matrix[i].length == n 1 <= n <= 20 -1000 <= matrix[i][j] <= 1000

Insight/Solution: Arrays + Linear Algebra (Transpose matrix) transpose the matrix, then reverse each row i.e. 1 2 3 4 5 6 7 8 9 apply transpose 1 4 7 2 5 8 3 6 9 apply reverse 7 4 1 8 5 2 9 6 3

54. Spiral Matrix Given an m x n matrix, return all elements of the matrix in spiral order. Constraints: m == matrix.length n == matrix[i].length 1 <= m, n <= 10 -100 <= matrix[i][j] <= 100

Insight/Solution: Arrays + Simulation you pretty much do what it literally asks, traverse in a spiral manner, which will require 4 pointers: top, left, right, and bottom the tricky part about this problem is the indexing and how to go about the simulation, but it can be easily thought when you have this mindset: after traversing some direction, we no longer can go that path, this may sound confusing but will make sense with an example at the start, we go top left to top right, so after traversing the top, we no longer need to access that top path, so we can increment our top pointer by 1, that is the idea we need to safely solve this problem

417. Pacific Atlantic Water Flow Given an m x n matrix of non-negative integers representing the height of each unit cell in a continent, the "Pacific ocean" touches the left and top edges of the matrix and the "Atlantic ocean" touches the right and bottom edges. Water can only flow in four directions (up, down, left, or right) from a cell to another one with height equal or lower. Find the list of grid coordinates where water can flow to both the Pacific and Atlantic ocean. Note: The order of returned grid coordinates does not matter. Both m and n are less than 150.

Insight/Solution: BFS or DFS, for me, more intuitive to think of it as a DFS problem use two visited sets, one for the pacific ocean and the other for atlantic ocean, from each border of either ocean, conduct DFS and accordingly determine which coordinates are reachable for each ocean pick the intersections of the visited sets as our solution

286. Walls and Gates You are given a m x n 2D grid initialized with these three possible values. -1 - A wall or an obstacle. 0 - A gate. INF - Infinity means an empty room. We use the value 231 - 1 = 2147483647 to represent INF as you may assume that the distance to a gate is less than 2147483647. Fill each empty room with the distance to its nearest gate. If it is impossible to reach a gate, it should be filled with INF.

Insight/Solution: BFS problem, we put all the locations of our gates into the queue, and conduct BFS, if the new room we are on has a larger value than our current room, set the new room's value to the current room's + 1 and add the new location into our queue

102. Binary Tree Level Order Traversal Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).

Insight/Solution: BFS, the most standard BFS algorithm on the tree

40. 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. Constraints: 1 <= candidates.length <= 100 1 <= candidates[i] <= 50 1 <= target <= 30

Insight/Solution: Backtracking + Sort the problem builds on 39. Combination Sum, where now we have the condition to only use one candidate, this also means we have to handle duplicates we can handle duplicates by sorting and checking for adjacent duplicates, which we would just skip if there are any, to handle using only one candidate, we keep track of a pointer and always advance our pointer after a backtrack call

90. Subsets II Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set). Note: The solution set must not contain duplicate subsets.

Insight/Solution: Backtracking + Sort, the problem is similar to 78. Subsets the key difference is that we cannot have duplicates, and the way to handle this case is to sort the numbers first, that way, when we have an order like [2, 1, 2], we can sort it to [1,2,2] and check if the adjacent element was a duplicate or not there is a catch though, there will be a case where we stumble upon [1,2] and [1,2], where each 2 is from the 1st and 2nd index of the array [1,2,2], also, [2,2] is a valid subset, we can handle this case by only skipping when we have reached this condition: if i > index and nums[i] == nums[i - 1] i can only be >= index to begin with, so why not i == index? we miss out on a subsets that have duplicates in the subset but isn't an actual duplicate subset, since we work in the manner of a top-down approach, so when we have a path of [1,2], then the remaining path we have left is [2], which would not ever be checked had i == index been part of the condition

111. Minimum Depth of Binary Tree Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. Note: A leaf is a node with no children.

Insight/Solution: DFS on a tree, similar to Maximum Depth of Binary Tree except we have extra conditions to check we have to check if a node has one child, that has to be dealt differently, for a clear idea, consider the degenerated linked list BT

51. 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. Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space, respectively. Constraints: 1 <= n <= 9

Insight/Solution: Backtracking, honestly a very standard backtracking problem also similar to letter combinations of a phone number, etc The additional "fluff" that we have to account for is validating a queen, and this depends on our implementation, for simplicity, assume that we fill in a queen every row the idea of our backtracking implementation is to assume that our previously placed queens are valid, so we can save time by only considering if the queen we just placed is valid, since we go row by row, we can already dismiss checking if there is another queen in the same row, since that will never happen if our implementation is correct instead, we should consider the up-down case and diagonal cases, for up-down case, we just simply check if a queen is in the same column as our newly placed queen, as for diagonals, best if you draw it out, but the solution is abs(row - i) == abs(col - j), where row and col represents the newly placed queen's location

77. Combinations Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. You may return the answer in any order. Constraints: 1 <= n <= 20 1 <= k <= n

Insight/Solution: Backtracking, idea is similar to permutations, letter combinations of a phone number, all the classic backtracking problems

131. 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. A palindrome string is a string that reads the same backward as forward.

Insight/Solution: Backtracking, really similar pattern to letter combinations of a phone number, permutations, and subsets

39. 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. It is guaranteed that the number of unique combinations that sum up to target is less than 150 combinations for the given input. Constraints: 1 <= candidates.length <= 30 1 <= candidates[i] <= 200 All elements of candidates are distinct. 1 <= target <= 500

Insight/Solution: Backtracking, similar to the classic problems Note: a simple optimization is instead of checking if target < 0, check in the for loop if candidates[i] > target, then continue instead, that way, we never have to call backtrack again if our target went negative

74. Search a 2D Matrix Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: Integers in each row are sorted from left to right. The first integer of each row is greater than the last integer of the previous row. Constraints: m == matrix.length n == matrix[i].length 1 <= m, n <= 100 -104 <= matrix[i][j], target <= 104

Insight/Solution: Binary Search there are two solutions using binary search: 1 with just a single binary search, the other with two binary searches both yield a complexity of O(lg(mn)) = O(lg(m) + lg(n)) using 1 binary search: treat the entire matrix as 1 sorted list by creating pivots, so r will be set to m * n - 1, and thus the pivot replaces the middle index, but the pivot is supposed to act like the middle index the pivot yields which number in the long sorted list to look at, and in terms of the matrix location, it will be stored in [pivot // n][pivot % n], now do your standard binary search using 2 binary searches: first find the row that can contain the target, which means that the target falls in between the row[0] and row[-1] elements once you find such a row, conduct a binary search on the row, your standard 1D binary search

33. Search in Rotated Sorted Array You are given an integer array nums sorted in ascending order, and an integer target. Suppose that nums is rotated at some pivot unknown to you beforehand (i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]). If target is found in the array return its index, otherwise, return -1. Constraints: 1 <= nums.length <= 5000 -10^4 <= nums[i] <= 10^4 All values of nums are unique. nums is guranteed to be rotated at some pivot. -10^4 <= target <= 10^4

Insight/Solution: Binary Search, wow this one was hard it is essentially binary search with extra cases, we have to handle for cases when we perform a standard binary search and also modified searches, the modified searches depend on the way the array has been pivoted the best way to approach the problem is simply considering how a normal binary search would handle such portions of a rotated sorted array, the easiest way to think about it is to consider the values of nums[left] or nums[right] for instance, if we search for 6 in [4,5,6,7,0,1,2], we see that nums[m] = 7, and since it is greater than target, we may have to consider a standard binary search (left portion), but to verify, we have to check if it could be in the right portion which is simply checking if nums[l] <= nums[m] and nums[l] > target, that is in fact, the only way we could have the target be in the right portion if nums[m] > target follow the same logic for the other case when nums[m] < target, and thats it

153. 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, return the minimum element of this array. Constraints: n == nums.length 1 <= n <= 5000 -5000 <= nums[i] <= 5000 All the integers of nums are unique. nums is sorted and rotated between 1 and n times.

Insight/Solution: Binary search We can move to the right/left of mid based off of if mid is greater/less than left. if mid > left: search right of mid, else: search left of mid. Keep track of lowest seen value for mid at all times and return the lowest.

191. Number of 1 Bits Write a function that takes an unsigned integer and returns the number of '1' bits it has (also known as the Hamming weight). Note: Note that in some languages such as Java, there is no unsigned integer type. In this case, the input will be given as a signed integer type. It should not affect your implementation, as the integer's internal binary representation is the same, whether it is signed or unsigned. In Java, the compiler represents the signed integers using 2's complement notation. Therefore, in Example 3 above, the input represents the signed integer. -3. Follow up: If this function is called many times, how would you optimize it? Constraints: The input must be a binary string of length 32

Insight/Solution: Bit manipulation, here we want to use several parts of bit manipulation: &, <<, and >> operators since the binary string has a length of 32, we can simply iterate 32 times and check if the bit we are on is a 1 or 0, use a mask to move and check the next bit, moving from right to left (use << operator) Follow-up: the & will compare the least significant bits of the two numbers, since Hamming weight requires the number of 1 bits, we know that 1 & 1 will yield 1, so we can simply compare the least significant bit of n and 1 to calculate how many 1 bits we have, afterwards, we have to move our n so that we move onto the next bit, which we can do with >> do this until n reaches 0 Note: there is another way, it is a bit manipulation trick where we continuously increment the number of bits and do n &= (n - 1) instead, this flips all the 1s to 0s, so once we reach n = 0, we have our Hamming weight

206. Reverse Linked List Reverse a singly linked list.

Insight/Solution: Can solve either iteratively or recursively Iteratively: initialize a curr pointer and prev pointer, iterate while curr is not null, continuously move curr.next to prev and prev to curr (will need a third pointer as well) recursively: idea is to work backwards once you reach the end of the linked list, remember to check for edge cases (when linked list size = 0, 1, 2)

17. 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 digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.

Insight/Solution: Classic DFS/Backtracking problem, store all possible phone combinations in a hash table and run a backtracking algorithm to create all possible combinations the implementation I have has 5 parameters: backtrack(digits, combinations, letter_map, path, index)

329. Longest Increasing Path in a Matrix Given an integer matrix, find the length of the longest increasing path. From each cell, you can either move to four directions: left, right, up or down. You may NOT move diagonally or move outside of the boundary (i.e. wrap-around is not allowed).

Insight/Solution: DFS + Memoization Brute Force approach is simply conduct a DFS and find the longest path, but that would take O(4^(mn)), what we can do though is keep track of the longest path for each cell (memoize), and that would drastically optimize the runtime to O(mn)

104. Maximum Depth of Binary Tree Given the root of a binary tree, return its maximum depth. A binary tree's maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

Insight/Solution: DFS on a tree Base case: An empty tree is essentially null, which would have a depth of 0. Outside of the base case, we would already have at least a depth of 1. Therefore, we can recursively solve the depth as: 1 + max(depth of left, depth of right)

130. Surrounded Regions Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'. A region is captured by flipping all 'O's into 'X's in that surrounded region. Explanation: Surrounded regions shouldn't be on the border, which means that any 'O' on the border of the board are not flipped to 'X'. Any 'O' that is not on the border and it is not connected to an 'O' on the border will be flipped to 'X'. Two cells are connected if they are adjacent cells connected horizontally or vertically.

Insight/Solution: DFS or BFS and coloring, the idea is to understand when will there not be a flip, turns out that if an 'O' starts at a border, it will be able to escape conduct DFS or BFS at those spots and color them as 'E' (for escaped), afterwards, iterate through the entire board, and if board[i][j] == 'E', flip it to 'O', otherwise if it is 'O', flip it to 'X'

494. Target Sum You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol. Find out how many ways to assign symbols to make sum of integers equal to target S. Constraints: The length of the given array is positive and will not exceed 20. The sum of elements in the given array will not exceed 1000. Your output answer is guaranteed to be fitted in a 32-bit integer.

Insight/Solution: DFS or DP problem, do yourself a favor and implement it using memoization instead of tabulation pretty easy to consider it as a DFS problem, but it will TLE O(2^n) instead, consider the problem like a DP knapsack 0/1 problem where we add up the number of ways every time, the memoization is super easy to add afterwards I repeat, use memoization. This problem killed my interest for solving DP problems using bottom-up... unless its like coin change or something

200. Number of Islands Given an m x n 2d grid 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. Constraints: m == grid.length n == grid[i].length 1 <= m, n <= 300 grid[i][j] is '0' or '1'.

Insight/Solution: DFS, we traverse through the grid, and if we hit grid[i][j] = '1', we increment our count by 1 and perform a DFS to change all the land connected to this spot with '*' (marking it as seen), that way, we can keep track of the number of islands we have

133. 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 val (int) and a list (List[Node]) of its neighbors. class Node { public int val; public List<Node> neighbors; } Test case format: For simplicity sake, each node's value is the same as the node's index (1-indexed). For example, the first node with val = 1, the second node with val = 2, and so on. The graph is represented in the test case using an adjacency list. Adjacency list is a collection of unordered lists used to represent a finite graph. Each list describes the set of neighbors of a node in the graph. The given node will always be the first node with val = 1. You must return the copy of the given node as a reference to the cloned graph. Constraints: 1 <= Node.val <= 100 Node.val is unique for each node. Number of Nodes will not exceed 100. There is no repeated edges and no self-loops in the graph. The Graph is connected and all nodes can be visited starting from the given node.

Insight/Solution: DFS/BFS + Hash Table, use the hash table to store the deep copies of each node, use DFS/BFS to traverse through the original node to make these copies

323. Number of Connected Components in an Undirected Graph 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 find the number of connected components in an undirected graph. Note: You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.

Insight/Solution: DFS/BFS, problem is essentially number of islands but using adjacency lists, since we are given only the edges, we have to build our adjacency list (use a hash table of lists), don't forget that it is undirected, so we have to add (v, u) in addition to (u, v) Keep track of our visited nodes in a boolean array and perform DFS, in our main loop, keep track of how many times we perform DFS, which will be the answer for the number of connected components in our graph

46. Permutations Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. Constraints: 1 <= nums.length <= 6 -10 <= nums[i] <= 10 All the integers of nums are unique.

Insight/Solution: DFS/Backtracking problem, for every number in nums, we branch out to continue forming a permutation without including the number we just added we can use either a set to keep track of which element we've already used or pass in our nums without the element we just added: nums[:i] + nums[i + 1:] if we just added nums[i] to our path

22. Generate Parentheses Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

Insight/Solution: DFS/Backtracking problem, we know that our combination must have a length of 2 * n since every pair has 2 characters for the opening and closing parentheses We have to make a choice whether to add '(' or ')' to our path, always start with '(' and keep count that we can only have n number of each parenthesis, helpful to use a left count and right count for the open and closed parentheses

78. Subsets Given an integer array nums, return all possible subsets (the power set). The solution set must not contain duplicate subsets. Constraints: 1 <= nums.length <= 10 -10 <= nums[i] <= 10

Insight/Solution: DFS/Backtracking, we essentially iterate through nums, update our path, and indicate where we should start with respect to the location we are currently on (start = i + 1, when we are at position i)

5. Longest Palindromic Substring Given a string s, return the longest palindromic substring in s. Constraints: 1 <= s.length <= 1000 s consist of only digits and English letters (lower-case and/or upper-case),

Insight/Solution: DP problem, build an N x N matrix where N = s.length, there are several cases to consider: dp[i][j] = { T, if i == j T, if i == j - 1 and s[i] == s[j] T, if dp[i + 1][j - 1] and s[i] == s[j] F, otherwise essentially, our 2D matrix will start with a diagonal of T, and we don't consider the left bottom portion of the matrix (due to symmetry), consider the columns as pointers to the end of a substring and the rows as the beginning pointers, so we should traverse in column major order, keep track of a substring when we satisfy a T condition of our recursive expression

322. Coin Change You are given coins of different denominations and a total amount of money amount. Write a function to compute 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.

Insight/Solution: DP problem, here is the recurrence relation: 0, if amount = 0 inf or amount + 1, if amount < 0 min of 1 <= k <= K (1 + coinChange(amount - coin)), otherwise; coin is one of the denominations in coin Essentially, we break down the number of coins we need with the type of coins we have, so if we have to make change for 6 with coins [1, 2, 5], we can determine that by 1 + number of ways to get 6 - 1, 6 - 2, and 6 - 5 Edge cases: if we can't get a certain amount, if our amount is 0

518. Coin Change 2 You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin.

Insight/Solution: DP problem, here is the recurrence relation: 1, if amount = 0 dp[i] + dp[i - coin], if coin >= i where we also initialize dp[i] = 0 the idea is to calculate the number of ways we get all amounts from 1 to amount each coin at a time, similarly with coin change, we can store our values in the manner [i - coin], since the number of ways we find a certain value depends what coin we have as well, we can add each time since for all the number of ways we can get dp[i - coin], we just have to throw in the coin value, so we keep building up to the amount of ways

62. Unique Paths A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below). How many possible unique paths are there? Constraints: 1 <= m, n <= 100 It's guaranteed that the answer will be less than or equal to 2 * 109.

Insight/Solution: DP problem, here is the recursive expression: 1, if m == 1 or n == 1 f(m - 1, n) + f(m, n - 1), otherwise essentially there can only be one path if m is 1 or n is 1, since that means we can only go in that one direction

572. Subtree of Another Tree Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and node values with a subtree of s. A subtree of s is a tree consists of a node in s and all of this node's descendants. The tree s could also be considered as a subtree of itself.

Insight/Solution: Recursion + DFS, first, travel down the bigger tree via standard dfs, if we find node equal to the value of root of the smaller tree, compare the subtrees. We travel down both subtrees at the same time and if and only if every node is the same then we know we have found the right subtree.

300. Longest Increasing Subsequence Given an integer array nums, return the length of the longest strictly increasing subsequence. A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements. For example, [3,6,2,7] is a subsequence of the array [0,3,1,6,2,2,7]. Follow up: Could you come up with the O(n2) solution? Could you improve it to O(n log(n)) time complexity?

Insight/Solution: DP problem, our subproblem is to find the longest subsequence without the value are currently on, in other words: dp[i] = max(dp[j])+1, ∀0≤j<i and num[i] > num[j] LIS_length​ = max(dp[i]), ∀0≤i<n Follow-up: the idea is that we can build another array called tails which holds the following invariant: (1) if x is larger than all tails, append it, increase the size by 1 (2) if tails[i-1] < x <= tails[i], update tails[i] also partially part of patience sort, we use binary search to find elements efficiently, note that since this algorithm is part of patience sort, we will only have the length of our subsequence correct, not the actual ordering of the subsequence (unless you use parent arrays and such modifications)

1143. Longest Common Subsequence Given two strings text1 and text2, return the length of their longest common subsequence. 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. (eg, "ace" is a subsequence of "abcde" while "aec" is not). A common subsequence of two strings is a subsequence that is common to both strings. If there is no common subsequence, return 0. Constraints: 1 <= text1.length <= 1000 1 <= text2.length <= 1000 The input strings consist of lowercase English characters only.

Insight/Solution: DP problem, personally think it is easiest to explain using a recursion tree here is the recursive expression: f(text1, text2) = { 0, if text1 == '' or text2 == '' 1 + f(text1[1:], text2[1:]), if text1[0] == text2[0] max(f(text1[1:], text2), f(text1, text2[1:])), otherwise the idea is that we do not have a subsequence if one of the strings is empty if the first character of both strings match, then we tally up that character and continue solving the subsequence without the first characters otherwise, we could have a subsequence either when one of the strings is a subsequence, and since we want the longest common subsequence, we should take the max of both sides

10. Regular Expression Matching Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*' where: '.' Matches any single character.​​​​ '*' Matches zero or more of the preceding element. The matching should cover the entire input string (not partial). Constraints: 0 <= s.length <= 20 0 <= p.length <= 30 s contains only lowercase English letters. p contains only lowercase English letters, '.', and '*'. It is guaranteed for each appearance of the character '*', there will be a previous valid character to match.

Insight/Solution: DP problem, pretty complicated but when broken down accordingly, the problem becomes intuitive first dive only into a case using '.' (not '*' yet), this is pretty simple, the recursive expression would be: T, if s[i] == p[j] or p[j] == '.' F, otherwise now we consider this '*' case, recognize that we must have a preceding letter before '*' so there must also be some length condition to check, but as for how it works, the '*' can do two things: negate all of the preceding letter's frequency or keep counting s[i], let's represent them as case *1 and case *2, note that case *2 requires s[i] == p[j] to keep track of the pair of preceding letter and '*', we consider p[j] = preceding letter and p[j + 1] = '*' therefore, our new recursive expression is now if p[j + 1] == '*', return f(s, p[j + 2:]) or (s[i + 1:], p[j + 1:]) else, consider the two cases with only '.' note: the recursive expression is not the only condition we have to check, we also have to ensure '*' comes after a preceding letter (determine via length condition), the only condition we do not care if s[i] == p[j] is for case *1 suggestion: solve using recursion first (main algorithm code in a helper function) and add memoization to the code, extremely simple and intuitive flow of the logic with this explanation compared to the bottom-up approach

213. 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 a list of non-negative integers nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police. Constraints: 1 <= nums.length <= 100 0 <= nums[i] <= 1000

Insight/Solution: DP problem, recognize that there can only be two solutions: either the total amount of money robbed includes the first house but excludes the last house or includes the last house but excludes the first house, whichever total sum is higher is the maximum amount of money we can rob knowing this, we can degenerate this problem into House Robber I where we have inputs nums[1:] and nums[:-1], then take the max of both of them Note: use a helper function to program the House Robber I function

673. Number of Longest Increasing Subsequence Given an integer array nums, return the number of longest increasing subsequences. Notice that the sequence has to be strictly increasing. Constraints: 1 <= nums.length <= 2000 -106 <= nums[i] <= 106

Insight/Solution: DP problem, similar to Longest Increasing Subsequence except we have an extra array to keep track of the number of longest increasing subsequences we do have to modify the inner part of the algorithm, we have to keep track of when we meet a new longest increasing subsequence or if we meet different longest increasing subsequence if length[i] == length[j], this implies that we have an even longer increasing subsequence, so we should update the length and also set count[i] = count[j] (keeps track of all the increasing subsequences), we can do this since we know for sure that nums[i] > nums[j], where j is the pointer that moves around and i is the pointer that points to the end of a subarray if length[i] == length[j] + 1, we know that the subsequence up to j is representing a different increasing subsequence which will also upgrade to be one of the longest increasing subsequences, so we simply do count[i] += count[j]

647. Palindromic Substrings Given a string, your task is to count how many palindromic substrings in this string. The substrings with different start indexes or end indexes are counted as different substrings even they consist of same characters.

Insight/Solution: DP problem, very similar to 5. Longest Palindromic Substring, in fact, the solution is exactly the same except we update our counter of amount of substrings instead of update the longest palindromic substring the recursive expression: dp[i][i] = True dp[i][i + 1] = True dp[i][j] = True, if dp[i + 1][j - 1] and s[i] == s[j] dp[i][j] = False, otherwise there is another solution using two pointers (expand from the center), think of the palindrome as an onion, as you peel its ends, what you have left is just a smaller onion, so if you work towards your center, you keep having smaller and smaller onions so we can start from the center of the substring and make our way towards the ends, keep counting as we form palindromes note that we have two types of palindromes, depending on their lengths: odd-lengths have a center size of 1 character and even-lengths have a center pair of 2 characters, so we can check both each time through a helper function

139. Word Break Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

Insight/Solution: DP problem, we can break up the problem in subproblems by considering the string when they have less characters, perhaps easiest explained using a recursive expression: True, if considering an empty string True, if dp[j] = True and s[j:i] is a valid word False, otherwise dp[j] has to be True since that's how we know that we're maintaining a valid word break

208. Implement a Trie (Prefix Tree) Implement a trie with insert, search, and startsWith methods. Note: You may assume that all inputs are consist of lowercase letters a-z. All inputs are guaranteed to be non-empty strings.

Insight/Solution: Design, just implement a trie insert: O(m) search: O(m) startsWith: O(m) m = length of word Real-life applications of a Trie: autocomplete, spell checker, IP routing (longest prefix matching), solving word games, T9 predictive text start by building the TrieNode class which will have a hash table and a boolean variable then simply build the Trie

91. Decode Ways A message containing letters from A-Z can be encoded into numbers using the following mapping: 'A' -> "1" 'B' -> "2" ... 'Z' -> "26" To decode an encoded message, all the digits must be mapped back into letters using the reverse of the mapping above (there may be multiple ways). For example, "111" can have each of its "1"s be mapped into 'A's to make "AAA", or it could be mapped to "11" and "1" ('K' and 'A' respectively) to make "KA". Note that "06" cannot be mapped into 'F' since "6" is different from "06". Given a non-empty string num containing only digits, return the number of ways to decode it. The answer is guaranteed to fit in a 32-bit integer. Constraints: 1 <= s.length <= 100 s contains only digits and may contain leading zero(s).

Insight/Solution: Dynamic Programming idea is to really use conditional statements to prevent any errors for instance, if there is a leading zero, it is instantly impossible to decode 0 cannot be a decoded letter in its own, and only numbers 10 and 20 are numbers that can be decoded and also have 0 as a digit the recursive expression has to do with: number of ways = { 0, if leading zero 1, if string is empty dp[1:], if string.length > 0 dp[1:] + dp[2:], if string.length >= 2 and dp[0:2] is a number between 10 and 26

338. Counting Bits Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array. Follow up: It is very easy to come up with a solution with run time O(n*sizeof(integer)). But can you do it in linear time O(n) /possibly in a single pass? Space complexity should be O(n). Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language.

Insight/Solution: Dynamic Programming, Bit Manipulation for each number, count the number of 1 bits and append to a result list Follow-up: the best way to see this is to get all the bit strings for the range 0 to 8, and when you take a look at numbers such as 3 and 6, their bit strings are 011 and 110, respectively these bit strings are shifted by 1! they have the same number of 1s, just shifted, which means we know that for even numbers, we can simply find the number of 1 bits by dividing the number by 2 and find the number of 1 bits for that number this isn't the case for odd numbers though, let's take a look at 2 and 3 which are 10 and 11, well... we just have an extra 1 bit for 3, and this makes sense since 3 = 2 + 1, so naturally we just add 1 to the number of bits for 2, and this works for all even numbers so we end up with this relationship dp[i] = { dp[i >> 1] or dp[i // 2], if i is even dp[i - 1] + 1, if i is odd note: i >> 1 is the same as i // 2, conversely, i << 1 is the same as i * 2

55. Jump Game Given an array of non-negative integers nums, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Determine if you are able to reach the last index. Constraints: 1 <= nums.length <= 3 * 104 0 <= nums[i] <= 105

Insight/Solution: Dynamic Programming, Greedy this problem is a good example of a problem that can be solved using either DP or greedy but the greedy is the optimal solution (usually DP is optimal and greedy is not) Using DP, we recognize that we just need to find all the combinations, so we can use backtracking + memoization, the time complexity would be O(n^2) Using greedy, recognize that we don't necessarily have to determine whether the non-maximum jumps yield to the last position or not, instead, we recognize that if we our current position + the maximum jump is greater than the last position, we know that the last position is possible to reach It would be wise to start from the end to the start as well, since that way, we can solve for the condition more efficiently so to set up the greedy algorithm: good_spot = nums.length - 1 then while iterating in reverse: if i + nums[i] >= good_spot, set good_spot = i if i == 0 at the end, we know that we can jump from the start to the end

416. Partition Equal Subset Sum Given a non-empty array nums containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal. Constraints: 1 <= nums.length <= 200 1 <= nums[i] <= 100

Insight/Solution: Dynamic Programming, in particular the 0/1 Knapsack Problem what we come to realize is we can decide whether or not to include an item in our knapsack, in this case, whether we pick a number to include in our subset But how do we what our weight limit should be for the subset? recognize that if we have 2 subsets of the same sum, then the total sum of the subset must be 2 * subset_sum, so we can easily calculate subset_sum by computing total_sum // 2, since we can only work with positive integers, we cannot find such a partition for two subsets if the total_sum is odd so the problem deduces down to finding a subset that adds up to half of the total_sum, and we have the power to decide whether we include an item in the subset or not our recursive expression then becomes: dp[0][0] = True, base case dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]], if j >= nums[i] where i represents the subset_sum and j represents the index of an item to pick for our subset

155. Min Stack Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. push(x) -- Push element x onto stack. pop() -- Removes the element on top of the stack. top() -- Get the top element. getMin() -- Retrieve the minimum element in the stack.

Insight/Solution: Either use one stack with tuples, or two stacks Tuples: (x, min_value of stack) Two Stacks: normStack: keeps track of all numbers pushed/popped, operates as normal minStack: Keeps track of smallest value for this "layer" of the stack. When pushing, push Math.min(currentMin, newVal). Keep in sync with "real" stack

143. Reorder List Given a singly linked list L: L0→L1→...→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→... You may not modify the values in the list's nodes, only nodes itself may be changed.

Insight/Solution: Fast and slow pointers this problem is a combination of 3 easy problems: Middle of the Linked List, Reverse a Linked List, and Merge 2 Linked Lists notice that the reordering we need is a merged linked list, where the latter linked list is in reverse order from the end we can assume, and even form examples, such that we recognize that we only have to reverse the right half of the linked list and merge it with the left half therefore, we find the middle of the linked list to cut our linked list into two linked lists, reverse the right half, then merge it with the left linked list

435. Non-overlapping Intervals Given a collection of intervals, find the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping. Note: You may assume the interval's end point is always bigger than its start point. Intervals like [1,2] and [2,3] have borders "touching" but they don't overlap each other.

Insight/Solution: Greedy + Intervals the problem is really similar to the activity selection problem, except whenever we do have an overlapping interval, we increment a count return the count afterwards

36. Valid Sudoku Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules: Each row must contain the digits 1-9 without repetition. Each column must contain the digits 1-9 without repetition. Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition. Note: A Sudoku board (partially filled) could be valid but is not necessarily solvable. Only the filled cells need to be validated according to the mentioned rules. Constraints: board.length == 9 board[i].length == 9 board[i][j] is a digit or '.'.

Insight/Solution: Hash set, and while not algorithmic, clean style of code to reiterate, we have three conditions to fulfill if we want to validate an initial sudoku board, and we should handle each condition with its own helper function, this will make the main loop look extremely simple, and in general, the code is very easy to read and follow along there really is no algorithmic thinking here, all you have to do is check if the value was already seen or not using a hash set, I would go for code organization if asked this question, the question is as simple as iterating through each element in a given row or column and checking if the value has already been seen for the nine 3 x 3 sub-box grids, iterate accordingly with appropriate start and end limits, start: 3i and end: 3(i + 1), for i = 0 to 3 since the Sudoku board never changes in size, everything is constant O(1)

49. 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. Constraints: 1 <= strs.length <= 104 0 <= strs[i].length <= 100 strs[i] consists of lower-case English letters.

Insight/Solution: Hash table (hashing), we can build our own hashing function to store words that are anagrams of each other, then simply return the values of the hash table as a list the key is how we will construct our hash function, we could sort the words and the sorted word is stored as a key, but we can actually do much better by counting the number of letters we see in the word we know that the letters are only lower case English letters, so we could build a fixed array of size 26 where each index represents a letter, so then you can count up the number of each letters and returning a tuple of the end result (summing them up fails, as that just means words of same length will be considered into the same hash key) simply use your newly constructed hash function as a hash key with a hash table, and return the values afterwards as a list

136. Single Number Given a non-empty array of integers nums, every element appears twice except for one. Find that single one. Follow up: Could you implement a solution with a linear runtime complexity and without using extra memory?

Insight/Solution: Hash table or set to store numbers already seen, initialize a count = 0 and add if we haven't seen the number yet, subtract if we have, leftover of count will be our answer Follow-up: Bit manipulation, use the XOR operator i.e. [4,1,2,1,2] -> 4 ^ 1 ^ 2 ^ 1 ^ 2 -> 4 ^ (1 ^ 1) ^ (2 ^ 2) -> 4 ^ 0 ^ 0 -> 4

57. Insert Interval Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). You may assume that the intervals were initially sorted according to their start times. Constraints: 0 <= intervals.length <= 104 intervals[i].length == 2 0 <= intervals[i][0] <= intervals[i][1] <= 105 intervals is sorted by intervals[i][0] in ascending order. newInterval.length == 2 0 <= newInterval[0] <= newInterval[1] <= 105

Insight/Solution: Interval you could solve this problem by appending the newInterval to intervals, then treating the problem like 56. Merge Intervals, but we can take advantage of the intervals already being sorted, that way, we maintain O(n) and downgrade to O(nlgn) simply just iterate through each interval until you find a suitable spot to place the newInterval, then keep merging for the rest of the intervals

56. 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. Constraints: 1 <= intervals.length <= 104 intervals[i].length == 2 0 <= starti <= endi <= 104

Insight/Solution: Interval + Sorting there are two kinds of interval merges: - [a,b], [c,d] and b >= c and d > b -> [a,d] - [a,b], [c,d] and b > c and b >= d -> [a,b] in other words, an interval either merges or the dominant one completely absorbs the other interval we can handle the intervals by first sorting them by start time, then merging accordingly by the start and end times of the intervals in question Note: to avoid having to pop or have indexing errors, use another list to hold the newly merged intervals

252. Meeting Rooms Given an array of meeting time intervals where intervals[i] = [starti, endi], determine if a person could attend all meetings. Constraints: 0 <= intervals.length <= 104 intervals[i].length == 2 0 <= starti < endi <= 106

Insight/Solution: Intervals + Sort the problem is really straightforward, and the idea can be similar to the activity selection problem, if we ever have an overlapping interval, we cannot attend all meetings now, the problem is that the intervals are out of order, so the best way to determine the eligibility is by sort, and in this case, we use a special sort in which we sort by the start time here is the expression: intervals[i][1] > intervals[i + 1][0], return False return True if all intervals do not overlap it is possible to do it by end time, but you would have to modify the expression

121. Best Time to Buy and Sell Stock Say you have an array for which the ith element is the price of a given stock on day i. If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit. Note that you cannot sell a stock before you buy one.

Insight/Solution: Iterate through the list from left to right and if we reach a smaller price than our left pointer, change our update left = right; else if prices[right] - prices[left] is greater than max_profit, update max_profit

1046. Last Stone Weight We have a collection of stones, each stone has a positive integer weight. Each turn, we choose the two heaviest stones and smash them together. Suppose the stones have weights x and y with x <= y. The result of this smash is: If x == y, both stones are totally destroyed; If x != y, the stone of weight x is totally destroyed, and the stone of weight y has new weight y-x. At the end, there is at most 1 stone left. Return the weight of this stone (or 0 if there are no stones left.) Note: 1 <= stones.length <= 30 1 <= stones[i] <= 1000

Insight/Solution: Max-heap, take the two largest stones, smash them together, and if there is still a leftover stone, put it back into the max-heap Continue to do this until the size of our heap reaches less than size 2, and return whatever stone is leftover if there was 1 stone left, other wise return 0 Note: Python's heap library uses min-heap implementation, we can easily convert it into a max-heap by simply multiplying the values of our stones by -1

112. Path Sum Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum. Note: A leaf is a node with no children.

Insight/Solution: Recursion + DFS, break down the tree into subtrees while also subtracting the sum if root is empty, return False if root.val == sum and root is a leaf, return True return f(root.left, sum - root.val) or f(root.right, sum - root.val)

235. Lowest Common Ancestor of a Binary Search Tree Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST. According to the definition of LCA on Wikipedia: "The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself)." Constraints: The number of nodes in the tree is in the range [2, 105]. -109 <= Node.val <= 109 All Node.val are unique. p != q p and q will exist in the BST.

Insight/Solution: Recursion + DFS, convince yourself that the LCA is the instant root.val falls in [p.val, q.val] or [q.val, p.val], it is, so the recursive expression is as follows: if p.val < root.val and q.val < root.val, return LCA(root.left, p, q) elif p.val > root.val and q.val > root.val, return LCA(root.right, p, q) return root Note: the root can also be a descendant of itself, meaning it can also be p or q, that is why we don't include <= or >= in the conditions

230. Kth Smallest Element in a BST Given a binary search tree, write a function kthSmallest to find the kth smallest element in it. Follow up: What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine? Constraints: The number of elements of the BST is between 1 to 10^4. You may assume k is always valid, 1 ≤ k ≤ BST's total elements.

Insight/Solution: Recursion + DFS, same idea as 98. Validate Binary Search Tree, do an inorder traversal but this time, keep decrementing k until 0, and return the node's value once k = 0 Follow-up: this modification we have to make matches a description of a database, a solution is to do something like an LRU cache and store a doubly linked list as well, something where a node of the BST points to its corresponding node to a doubly linked list which is used for searching this follow-up is more of a design problem, another advanced data structure (which is also used in database managements) is the B+ tree

236. Lowest Common Ancestor of a Binary Tree Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According to the definition of LCA on Wikipedia: "The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself)." Constraints: The number of nodes in the tree is in the range [2, 105]. -109 <= Node.val <= 109 All Node.val are unique. p != q p and q will exist in the tree.

Insight/Solution: Recursion + DFS, similar to 235 which uses BSTs, except we do not have the special property that BST has so instead, let's take a closer look at the trees, the LCA is the node that is deepest compared to the other CAs, but what does that mean in terms of recursion? it is the first node that joins p and q from different sides, since recursion works such that we go deep, then bubble back up, so if we bubble back up to a node that satisfies the CA condition, we guarantee it is also the LCA the edge happens when one of the descendants is its own ancestor, which we handle that if we find a left node but not a right node (or right node but not a left node), we assume the other node must be in the left subtree or right subtree (whichever was returned), this is because our constraint says p and q exist in the tree, so we would rule that the node we haven't found yet is part of the subtree we would return this question is a logically thinking based problem I would say

100. Same Tree Given the roots of two binary trees p and q, write a function to check if they are the same or not. Two binary trees are considered the same if they are structurally identical, and the nodes have the same value. Constraints: The number of nodes in both trees is in the range [0, 100]. -104 <= Node.val <= 104

Insight/Solution: Recursion + DFS, the only way a tree is the same is if both are empty or both have the same value, and they both must have the same tree structure any other time means it is false, so we could set up our recursion as follows if p and q are both empty, return True if only one of them is empty, return False if p.val != q.val, return False once you check all these conditions, return f(p.left, q.left) and f(p.right, q.right)

543. Diameter of a Binary Tree Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root. Note: The length of path between two nodes is represented by the number of edges between them.

Insight/Solution: The idea is to use DFS which will bring the diameter to us as a side effect We calculate the depth of a tree, where we actually start with a depth of -1 for a solo tree node. Build up the left and right subtree depths + 1, sum them together for our diameter. However, we should return the max of depths for left and right, since the diameter encompasses both paths, while we can only account for one full path when returning.

19. Remove Nth Node From End of List Given the head of a linked list, remove the nth node from the end of the list and return its head. Follow up: Could you do this in one pass?

Insight/Solution: Sentinel Node + two pointers, the question presents itself well as a puzzle, but the edge cases are very difficult to handle unless a sentinel node is used A two-pass algorithm consists of determining the length of the linked list first, then subtracting the length by n, then iterating until the length reaches 0 with another pointer at head, every iteration, we decrement length by 1, once length reaches 0, we simply have our pointer's next point to pointer.next.next Follow-up: Notice that the nth node from the end and the end of the linked list (which we will consider as the null pointer after the last valid node) has a gap of n + 1, so initially, we could iterate only our fast pointer while incrementing a variable gap which is initially set to 0, once gap reaches n + 1, we can start moving our slow pointer, once our fast pointer reaches null, our slow pointer is at the node right before the nth node from the end, so we can simply do slow.next = slow.next.next altogether, we return sentinel.next which handles the edge cases very neatly for us

2. Add Two Numbers You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. You may assume the two numbers do not contain any leading zero, except the number 0 itself. Constraints: The number of nodes in each linked list is in the range [1, 100]. 0 <= Node.val <= 9 It is guaranteed that the list represents a number that does not have leading zeros.

Insight/Solution: Sentinel node, this problem is really an issue with edge cases start off with a sentinel node, then keep building the linked list with the sum of both linked list values and a carry, the carry should be set to 1 if the original sum >= 10 otherwise 0, and when we construct the linked list node, we should set the value as total sum (including the carry) % 10, make sure to account for when a linked list is longer than the other, and if the carry ends up pushing to the absolute end of the linked list an example of such edge cases: 2->9 7->8->4 or 2->9 7->8->9

739. Daily Temperatures Given a list of daily temperatures T, return a list such that, for each day in the input, tells you how many days you would have to wait until a warmer temperature. If there is no future day for which this is possible, put 0 instead. For example, given the list of temperatures T = [73, 74, 75, 71, 69, 72, 76, 73], your output should be [1, 1, 4, 2, 1, 1, 0, 0]. Note: The length of temperatures will be in the range [1, 30000]. Each temperature will be an integer in the range [30, 100].

Insight/Solution: Stack or Queue Create a stack of tuples (temp, index) Create a res array of all 0's iterate the whole array while the current temp is larger than the top of the stack, update the correct index in the result array with the delta between the current index and the index from the current top of stack pop the stack add the current val to the stack continue until we reach the end. O(n) time, O(n) space

424. Longest Repeating Character Replacement Given a string s that consists of only uppercase English letters, you can perform at most k operations on that string. In one operation, you can choose any character of the string and change it to any other uppercase English character. Find the length of the longest sub-string containing all repeating letters you can get after performing the above operations. Note: Both the string's length and k will not exceed 104.

Insight/Solution: Sliding Window + Hash Table, the idea is that our substring should contain the letter with the highest frequency since that will maximize our length with our k operations, this is why we use a hash table (or a counter), then we should also consider the case when should we move our left pointer the sliding window case should happen when we know we ran out of k operations, so we move our left pointer to the right by one, however, we also have to keep track of our letter counts, so we will also decrement count[s[i]] by 1, when i = left pointer the condition to check for this sliding window case is if our current window length is greater than the maximum frequency of a character + k, so we should also keep another variable to track the maximum frequency as we iterate throughout the string after we reach the end of our string, i will represent the start of our substring, so we can simply return the length of the string - i to get the length of our maximum substring

3. Longest Substring Without Repeating Characters Given a string, find the length of the longest substring without repeating characters. Constraints: 0 <= s.length <= 5 * 104 s consists of English letters, digits, symbols and spaces.

Insight/Solution: Sliding Window + Set Iterate through w/p1 Each loop, check if the cur char exists in the set. Remove the char at p2 and p2++. Loop until we have removed the duplicate. as we iterate, keep track of the global max we've seen in a "no duplicates" state of the window

904. Fruit Into Baskets In a row of trees, the i-th tree produces fruit with type tree[i]. You start at any tree of your choice, then repeatedly perform the following steps: Add one piece of fruit from this tree to your baskets. If you cannot, stop. Move to the next tree to the right of the current tree. If there is no tree to the right, stop. Note that you do not have any choice after the initial choice of starting tree: you must perform step 1, then step 2, then back to step 1, then step 2, and so on until you stop. You have two baskets, and each basket can carry any quantity of fruit, but you want each basket to only carry one type of fruit each. What is the total amount of fruit you can collect with this procedure? Note: 1 <= tree.length <= 40000 0 <= tree[i] < tree.length

Insight/Solution: Sliding Window, same problem as 159. Longest Substring with At Most Two Distinct Characters except using integers instead of characters this question prompt is really poorly written, so just read 159's question prompt for effectively the same problem

567. Permutation in String Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1. In other words, one of the first string's permutations is the substring of the second string. Constraints: The input strings only contain lower case letters. The length of both given strings is in range [1, 10,000].

Insight/Solution: Sliding window + counter + auxiliary state variable, literally the same problem as 438. Find All Anagrams in a String except we return True/False and immediately if one permutation (anagram) was found here, the sliding window will be fixed with the length of s1, our counter will be an array of size 26, all initialized to 0, our auxiliary state will also be set to the length of s1, and our opening pointer set to 0 we set up our counter array such that each character's frequency is counted, i.e. [a: 1, b: 2, c: 0, d: 2, etc...], now we can iterate throughout the string s and maintain the fixed window length every iteration, we decrement the count of the current character in our counter array, notice that the characters in s1 will all be greater than 0, so we know that if its counter value is >= 0 after its decrement, we know that we should update our auxiliary state variable since that means we have a one less character to consider for our window if our auxiliary state variable reaches 0, we know that we've found a window that is a permutation of s1 afterwards, we have to check if our window's length == len(s1), and if it is, we have to update our array counter by 1 for the current letter we are on, also update our auxiliary state variable accordingly (ensuring that we only update the letters in s1), and move our opening pointer by 1

234. Palindrome Linked List Given a singly linked list, determine if it is a palindrome. Follow up: Could you do it in O(n) time and O(1) space?

Insight/Solution: Store node values in an array and check if the reverse is the same as the original Follow up: Two pointers + reverse linked list, we have a slow and fast pointer, iterate until fast or fast.next is null, then reverse the linked list at slow, then reset fast to head, then iterate and check if fast and slow are the same

76. Minimum Window Substring Given two strings s and t, return the minimum window in s which will contain all the characters in t. If there is no such window in s that covers all characters in t, return the empty string "". Note that If there is such a window, it is guaranteed that there will always be only one unique minimum window in s. Constraints: 1 <= s.length, t.length <= 105 s and t consist of English letters. Follow up: Could you find an algorithm that runs in O(n) time?

Insight/Solution: Sliding window + hash table + auxiliary state variable, but let's first go over the brute force solution Brute force: find all the substrings and simply check if the characters in t are in the substring, should take O(S^3) Sliding Window: optimized to O(S + T), this problem is very similar to permutation in string and find all anagrams in a string, the idea is to keep the auxiliary state variable and decrement it whenever we land on a character that has yet to be decremented in our hash table, then we also keep track of a min_length and min_window to keep track of the minimum substring a difference we see for this problem compared to permutation in string and find all anagrams in a string is that our window size can vary, meaning we have to accommodate our logic to handle a window with characters that do not belong in the t string, which is okay (instead of an if statement, use a while loop)

141. Linked List Cycle 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.

Insight/Solution: Slow and fast pointers, the fast pointer starts one node after the slow node and moves one extra node further each time if slow == fast: there is a cycle if fast is null or fast.next is null, there is no cycle

253. Meeting Rooms II Given an array of meeting time intervals intervals where intervals[i] = [starti, endi], return the minimum number of conference rooms required. Constraints: 1 <= intervals.length <= 104 0 <= starti < endi <= 106

Insight/Solution: Sort + Heap + Intervals very similar to 252 but with a twist, we have to keep track of all the rooms required the idea with sort and interval from 252 will continue to apply for 253, the new additional problem is we have to keep track of the number of rooms and how recognize that if we simply come up with a room that doesn't overlap an interval, we know that it can replace the room that just finished, this room will also have to be the room with the fastest (smallest?? idk the word to use here) end time, and we can easily keep track via a min heap, and as a result, the min heap acts as a room counter in itself once we go through all intervals, we can simply return the length of our min heap which will represent the number of rooms

15. 3Sum Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero. Notice that the solution set must not contain duplicate triplets. Constraints: 0 <= nums.length <= 3000 -105 <= nums[i] <= 105

Insight/Solution: Sort + Two Pointers Notice that a + b = -c, so we could downgrade this problem into two sum, however, since we do have duplicates to consider, we can easily handle duplicates by sorting and skipping adjacent duplicates so actually, the problem downgrades to Two Sum sorted Sort array of nums For each num (skip cur if prev is the same cur), execute 2sum sorted on the rest of the array 2sum sorted: 2 pointers, cur > sum : right-- cur < sum: left++ cur == sum: pop to output Remember to skip nums if they are the same as their prev

217. Contains Duplicate Given an array of integers, find if the array contains any duplicates. Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.

Insight/Solution: Sort or use a hash table/hash set, for sort check if adjacent elements are equal, for set check if it is already in the set sorting is O(nlgn) and hash table/hash set is O(n)

268. Missing Number Given an array nums containing n distinct numbers in the range [0, n], return the only number in the range that is missing from the array. Follow up: Could you implement a solution using only O(1) extra space complexity and O(n) runtime complexity?

Insight/Solution: Sort, Hash Set, bit manipulation, Gauss' Formula Follow-up: bit manipulation and Gauss' Formula handle this follow-up, for bit manipulation, recognize that there is guaranteed a missing number, but we can collect all the numbers of the range from our index and length of the nums (if the missing number is simply n), then any number that is not missing can be shown twice, while the missing number is shown once, using the XOR operator, a ^ a = 0, b ^ 0 = b, and it has the commutative property as well, so simply XORing will yield the correct solution

269. Alien Dictionary There is a new alien language that uses the English alphabet. However, the order among letters are unknown to you. You are given a list of strings words from the dictionary, where words are sorted lexicographically by the rules of this new language. Derive the order of letters in this language, and return it. If the given input is invalid, return "". If there are multiple valid solutions, return any of them. Constraints: 1 <= words.length <= 100 1 <= words[i].length <= 100 words[i] consists of only lowercase English letters.

Insight/Solution: Topological Sort, there are two steps to solving this hard LC problem 1. build a DAG 2. topological sort simple idea, difficult execution, the difficulty comes from building the DAG and handling some interesting edge cases, understand what it means for a word to be lexicographically correct, here is an example: abc, zebra, zoo z > o but lexicographically cares about the order of words, not individual letters, we should only care about letters at its individual level for an example like this: wrt, wrf --> this means t comes before f an edge case to consider is abc, ab which the expected outcome is an empty string if abc is before ab, then c must have some negative order value which doesn't make sense, so we declare this test case invalid using Kahn's algorithm with ascii value indexing will be efficient, easy, and clean

210. Course Schedule II There are a total of n courses you have to take labelled from 0 to n - 1. Some courses may have prerequisites, for example, if prerequisites[i] = [ai, bi] this means you must take the course bi before the course ai. Given the total number of courses numCourses and a list of the prerequisite pairs, 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 finish all courses, return an empty array. Constraints: 1 <= numCourses <= 2000 0 <= prerequisites.length <= numCourses * (numCourses - 1) prerequisites[i].length == 2 0 <= ai, bi < numCourses ai != bi All the pairs [ai, bi] are distinct.

Insight/Solution: Topological Sort, this problem is a topological sort problem in disguise, you can also use Kahn's algorithm which is topological sort using in-degrees

199. Binary Tree Right Side View Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.

Insight/Solution: Tree + BFS, conduct a level order traversal and the last node in each level is the node we see from the right side

167. Two Sum II - Input array is sorted Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number. The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Note: Your returned answers (both index1 and index2) are not zero-based. You may assume that each input would have exactly one solution and you may not use the same element twice. Constraints: 2 <= nums.length <= 3 * 104 -1000 <= nums[i] <= 1000 nums is sorted in increasing order. -1000 <= target <= 1000

Insight/Solution: Two pointers, if nums[l] + nums[r] > total, then move r pointer, if <, move l pointer, else you found a match

704. Binary Search Given a sorted (in ascending order) integer array nums of n elements and a target value, write a function to search target in nums. If target exists, then return its index, otherwise return -1. Note: You may assume that all elements in nums are unique. n will be in the range [1, 10000]. The value of each element in nums will be in the range [-9999, 9999].

Insight/Solution: binary search

105. Construct Binary Tree from Preorder and Inorder Traversal Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that duplicates do not exist in the tree.

Insight/Solution: Tree + DFS + Queue + Hash Table, the theory is that preorder goes root -> left -> right, and inorder goes left -> root -> right what this means in the arrays is the first node in the preorder array is our root, and the subarray to the left of that value in the inorder array is a left subtree of the root, same goes for the right subarray consider the preorder: [20, 15, 7] and inorder: [15, 20, 7], this means that 20 is the root, 15 is the left subtree, and 7 is the right subtree how to handle the preorder condition? this is where the queue and DFS comes into play, we have to use queue's popleft property to pop off the first element of the preorder array, and we have to use DFS so that we maintain our mutable array throughout our recursive path, if we have a larger tree to consider such as preorder: [3,9,20,15,7] and inorder: [9,3,15,20,7], you'll notice that we want to consider the right subtree once we pop off everything to the left subtree as well, this is why we must do DFS so that we take advantage of the mutable property that lists have in python use a hash table to keep track of where each element of inorder is, we use the index that we get as a way to split up our array into subproblems and eventually reach a base case to terminate our recursion Note: implement the algorithm with just DFS first, then make the small adjustments to optimize

98. 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.

Insight/Solution: Tree + DFS, one key principle about BSTs is that its inorder traversal yields a sorted output, so if we conduct an inorder traversal, we can simply check if the previous value was smaller than the current value remember that the inorder traversal follows the rule: left->root->right, you can solve this iteratively or recursively, but if done iteratively, use the stack data structure Can also DFS and keep track of "left" and "right" bounds for each node value.

211. 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. Constraints: 1 <= word.length <= 500 word in addWord consists lower-case English letters. word in search consist of '.' or lower-case English letters. At most 50000 calls will be made to addWord and search.

Insight/Solution: Trie + DFS, essentially a direct application of a Trie data structure, except we tweak the search method to accommodate for the wildcard '.' use DFS to search, and to handle the wildcard, simply iterate through each letter in the links of the current trie node and perform DFS

11. 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. Constraints: n == height.length 2 <= n <= 3 * 104 0 <= height[i] <= 3 * 104

Insight/Solution: Two Pointers area = w * h w = r - l h = min(height[l], height[r]) area = max(area, w * h) then increment l if height[l] < height[r], else decrement r the idea is to recognize that although the width is varying, we know that the heights skew significantly, and we can only cap the area of the rectangle by the minimum height, there is a chance we could encounter a really large height after the smaller height, which is why we maintain the max height instead, its like investing into the future

79. Word Search Given an m x n board and a word, find if the 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. Constraints: m == board.length n = board[i].length 1 <= m, n <= 200 1 <= word.length <= 103 board and word consists only of lowercase and uppercase English letters.

Insight/Solution: Use DFS/backtracking, we have the backtracking function return a boolean, indicating whether we have a match or not, a match is happened if we run out of letters to search for, any time we go out of the board or letters don't match we return false we have to use backtracking so we don't accidentally go back to our previous letter, use a character like '*', and revert back to the original letter after backtracking, this means we should store our current letter in a temp variable afterwards, return the matched boolean variable, if it trickles back to the top as true, we found a word match

21. Merge Two Sorted Lists Merge two sorted linked lists and return it as a new sorted list. The new list should be made by splicing together the nodes of the first two lists.

Insight/Solution: Use a sentinel node and update accordingly while l1 and l2 Note: remember to add the last node after the while loop iteration and return the next node of our sentinel node

242. Valid Anagram Given two strings s and t , write a function to determine if t is an anagram of s. Note:You may assume the string contains only lowercase alphabets. Follow up: What if the inputs contain unicode characters? How would you adapt your solution to such case?

Insight/Solution: Use an array of size 26 to keep track of counts, the index is based off the character, so we start at 'a' = 0 -> 'z' = 25, for every iteration of a letter for s and t, we increment by 1 and decrement by 1, respectively iterate through the array again and if we find a value not equal to 0, we know it means s and t were not anagrams Follow-up: use a hash table instead of a fixed-size array

261. 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. Note: you can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0,1] is the same as [1,0] and thus will not appear together in edges.

Insight/Solution: Very similar problem to Course Schedule with a few more constraints, in graph theory, a graph is also a tree with the following: - the graph must be acyclic - the graph must be connected in Course Schedule, we already handled the cycle detection, in addition, we have to check if we visit all nodes or not (therefore, we should only call DFS once), and since we consider the graph undirected, we also have to pass an extra parameter (parent) to check if our vertex is a parent (if so, we should not consider it, or else our algorithm will consider it a cycle)

1. 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.

Insight/Solution: a + b = x, so use a hash table to check if b = x - a is in it

53. 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.

Insight/Solution: can use either greedy or DP Greedy: locally solve the optimal solution and constantly compare it with the global solution DP: Kadane's Algorithm where f(i) = maximum subarray up to i f(1) = a1 f(i) = ai + max(0, f(i - 1)) Note: DP solution requires O(n) space if we cannot modify the input array

70. 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?

Insight/Solution: classic DP problem, essentially fibonacci sequence since we can take a step of size 1 or 2, we can produce this recursive expression dp[i] = 1 if i = 0 or i = 1 dp[i] = dp[i - 1] + dp[i - 2] if i > 1

215. Kth Largest Element in an Array Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element. Note: You may assume k is always valid, 1 ≤ k ≤ array's length.

Insight/Solution: heap, can actually be solved using either a max heap or min heap, but using a min heap will be optimal with a max heap: initialize a max heap with the nums and pop the heap k times, and the kth element is the answer with a min heap: initialize an empty heap and construct a min heap with the values, but as we keep adding them, we also pop out values if our min heap's size exceeds k, this effectively will maintain the largest elements of the heap up to size k, and we can just pop once more after iterating to get the kth largest element Note: python's heapq module includes a function that does this already: heapq.nlargest(k, nums) which outputs an array sorted in descending order, there is also a function for smallest (heap.nsmallest) analysis: max-heap's time and space complexities are as follows: O(nlgn) and O(n), for the min-heap, they are as follows: O(nlgk) and O(k) There is in fact an even better solution in O(n) and O(1), it is a divide and conquer technique called Hoare's selection algorithm (Quickselect), something Professor Miguel also taught to us, need to look in the future and implement using this algorithm

238. Product of Array Except Self Given an array nums of n integers where n > 1, return an array output such that output[i] is equal to the product of all the elements of nums except nums[i]. Constraint: It's guaranteed that the product of the elements of any prefix or suffix of the array (including the whole array) fits in a 32 bit integer. Note: Please solve it without division and in O(n). Follow up: Could you solve it with constant space complexity? (The output array does not count as extra space for the purpose of space complexity analysis.)

Insight/Solution: left and right product lists, we basically construct an array for the product of numbers to an element's left and it's right, afterwards, multiply each element at left with the corresponding right and store into an output array Follow-up: same approach as before, except we don't store the products in an array, instead, we multiply an integer variable as we move forward, so suppose we start with our left products, then as we move along our output array, we set it to left, then multiply left by the current location we are at (use an example, it will make more sense), do the same approach with right products except we go in reverse and multiply output[i] with right, not set output[i] = right Note: the two extremes at the end only require a product from one side, the far left element does not have any left products, the far right element does not have any right products, use 1 as a fill-in value due to the identity property of multiplication

152. Maximum Product Subarray Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.

Insight/Solution: one of those weird DP/greedy problems, we keep track of local solutions and build our global solution off the local solutions Similar idea to the maximum subarray problem, except we handle products which have a twist, the twist has to do with handling two cases: zeroes and negative numbers recall that the greedy choice in the maximum subarray problem was local_max = max(num, num + local_max) global_max = max(local_max, global_max) the idea is a great start, so consider for this problem the greedy choice foundation local_max = max(num, num * local_max) global_max = max(local_max, global_max) handling negative numbers means there is a potential for a local_max to suddenly switch to being the smallest product, and the opposite is true too, the smallest product can suddenly switch to being the largest product, so it would be wise to keep track of the smallest products as well, and consider their product with num as another condition for checking the max so now we have local_max = max(num, num * local_max, num * local_min) local_min = min(num, num * local_max, num * local_min) global_max = max(local_max, global_max) zeroes are handled accordingly with the condition of checking num and multiplying by num, that will effectively reset our multiplication chain and start fresh for the next number, our global_max will still hold onto the max product we solved before the zero, so if there is an even larger max product after the zero, our third line for computing the global_max will handle that case accordingly

20. 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.

Insight/Solution: the idea is that the most recent open parenthesis will match the first closing parenthesis, hence we can use a LIFO data structure Use a stack to add all the open parentheses and pop whenever we hit a closed parenthesis.

207. Course Schedule There are a total of numCourses courses you have to take, labeled from 0 to numCourses-1. Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1] Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses? Constraints: The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented. You may assume that there are no duplicate edges in the input prerequisites. 1 <= numCourses <= 10^5

Insight/Solution: this problem is disguised as a graph cycle detection problem, recognize that we could build our pairs as a directed graph Notice that we can have disconnected graphs, and that we only fail to complete all courses if there is a cycle, use DFS with the labeling of -1, 0, and 1 instead 0 = unvisited -1 = visiting 1 = visited this is important to note because if we had done a standard T/F method, our algorithm will allow some edges to be considered as a cycle (which may or may not be true)

226. Invert Binary Tree Invert a binary tree.

Insight/Solution: very easily done via recursion (idea that trees have subtrees, if they have children) if the head is null, return it head->left = inverse of head->right head->right = inverse of head->left can do this in one line with python

287. Find the Duplicate Number Given an array of integers nums containing n + 1 integers where each integer is in the range [1, n] inclusive. There is only one duplicate number in nums, return this duplicate number. Follow-ups: How can we prove that at least one duplicate number must exist in nums? Can you solve the problem without modifying the array nums? Can you solve the problem using only constant, O(1) extra space? Can you solve the problem with runtime complexity less than O(n2)? Constraints: 2 <= n <= 3 * 104 nums.length == n + 1 1 <= nums[i] <= n All the integers in nums appear only once except for precisely one integer which appears two or more times.

Insight/Solution: we'll break up the solutions by their follow-ups: you can sort the list and check for adjacent duplicates 1.) pigeonhole principle, if n objects are put into m containers, and n > m, then there will be at least one container with multiple objects 2). since sorting will modify the list, we can use a hash set instead to store the numbers we have seen in the array 3). since using a hash set means we use space, we have to opt for a different algorithm, this one allows for the brute force, so simply run a nested for-loop 4). rip, they said now make it faster, we can use Floyd's Tortoise and Hare algorithm (phase 1: tortoise = nums[tortoise], hare = nums[nums[hare]], phase 2: both move at the same pace), essentially, we treat the value of our array as pointers to nodes


Conjuntos de estudio relacionados

Product Liability and Warranties

View Set

W.10 Project resource management

View Set

Chapter 7 - Ethical Decision Making

View Set

Chapter 7 Skeletal System Assignment

View Set

Quiz 5 GOVT-2306-73743, Quiz 6 GOVT-2306-73743, Quiz 7 GOVT-2306-73743, Quiz 8 GOVT-2306-73743

View Set

HubSpot Inbound Marketing Certificate

View Set