Technical Interviews

Lakukan tugas rumah & ujian kamu dengan baik sekarang menggunakan Quizwiz!

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.

BACKTRACK: At the beginning, first we check if we reach the bottom case of the recursion, where the word to be matched is empty, i.e. we have already found the match for each prefix of the word. We then check if the current state is invalid, either the position of the cell is out of the boundary of the board or the letter in the current cell does not match with the first letter of the word. If the current step is valid, we then start the exploration of backtracking with the strategy of DFS. First, we mark the current cell as visited, e.g. any non-alphabetic letter will do. Then we iterate through the four possible directions, namely up, right, down and left. The order of the directions can be altered, to one's preference. At the end of the exploration, we revert the cell back to its original state. Finally we return the result of the exploration.

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

BACKTRACK: Here is a backtrack function which takes the index of the first integer to consider as an argument backtrack(first). If the first integer to consider has index n that means that the current permutation is done. Iterate over the integers from index first to index n - 1. Place ith integer first in the permutation, i.e. swap(nums[first], nums[i]). Proceed to create all permutations which starts from ith integer: backtrack(first + 1). Now backtrack, i.e. swap(nums[first], nums[i]) back.

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.

BACKTRACK: Here we define a recursive function of backtrack(remain, comb, start) (in Python), which populates the combinations, starting from the current combination (comb), the remaining sum to fulfill (remain), and the current cursor (start) to the list of candidates. For the first base case of the recursive function, if the remain==0 (i.e. we fulfill the desired target sum), we can add the current combination to the final list. As another base case, if remain < 0 (i.e. we exceed the target value), we will cease the exploration here. Other than the above two base cases, we would then continue to explore the sublist of candidates as [start...n]. For each of the candidates, we invoke the recursive function itself with updated parameters. Specifically, we add the current candidate into the combination. With the added candidate, we now have less sum to fulfill, i.e. remain - candidate. For the next exploration, still we start from the current cursor start. At the end of each exploration, we backtrack by popping out the candidate out of the combination.

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

BACKTRACK: Use a recursive helper function called backtrack(List[str] ans, str cur, int open, int close, int max). ans is the final list of all solutions, cur is the current string for the recursive call, open is the number of open parentheses in cur, close is the number of close parentheses in cur, max is the number of pairs of parentheses. In the recursive call, if cur is the final length, add the answer to the list and return. If open < max, we can add another "(", so recurse. If close < open, we can add another ")", so recurse.

String Permutations by Changing Case: Given a string s, you can transform every letter individually to be lowercase or uppercase to create another string. Return a list of all possible strings we could create. Return the output in any order.

BACKTRACK: Use a recursive helper function. Convert the string to a char array for constant time lookup. If current char is a digit, recurse with the char added to next index spot. Else, recurse with lowercase version of char and uppercase version of char added to next index spot. If at any point, the char array is equal to the length of the string, add new str(charArr) to list.

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.

BFS, HASHING: Use a set to put in all the words from word list. Use a queue and add the beginning word. Use a level variable to keep track of final return value int. For every string in the queue (since they will all be the same level), poll from queue and convert string to a char array. Use a for loop from 'a' to 'z' to change each letter of the char array and check all possible transformations. If the transformation equals the final word, return level + 1. If the transformation is in the set, add it to the queue and remove it from the set (to avoid duplicates). At the end, increment level. If the queue is ever empty without finding the word, return 0 for not finding the word. Optimization: bi-directional BFS which meets in the middle.

Binary Tree Right Side View Given the root of 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.

BFS, LEVELORDER TRAVERSAL: Do a levelorder traversal and grab the last node of each level to add to the rightside list. Initiate the list of the right side view rightside. Initiate the queue by adding a root. While the queue is not empty: Write down the length of the current level (level_length = queue.size()); iterate over i from 0 to level_length - 1, popping the current node from the queue (node = queue.poll()); if i == level_length - 1, then it's the last node in the current level, push it to rightsize list. Add first left and then right child node into the queue. Return rightside.

Binary Tree Levelorder Traversal: Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level).

BFS, QUEUE: Create a queue and push the root into the queue. While the queue isn't empty, set the levelSize to be the size of the queue. Use a for-loop up to levelSize to iterate through a level, popping off from the queue, adding this value to the list, and adding the left and right children to the queue if they are not None. Outside of each for-loop, add the level to the wrap list.

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.

BFS: Use a queue. Keep track of a depth variable. Add the root and while the queue isn't empty, check the size of the queue (how many leaves are in this level). Iterate through this level; if a node is a leaf, return depth immediately. Otherwise, add the children to the queue. Increment depth and repeat the loop.

The K Weakest Rows in a Matrix: You are given an m x n binary matrix mat of 1's (representing soldiers) and 0's (representing civilians). The soldiers are positioned in front of the civilians. That is, all the 1's will appear to the left of all the 0's in each row. A row i is weaker than a row j if one of the following is true: The number of soldiers in row i is less than the number of soldiers in row j OR both rows have the same number of soldiers and i < j. Return the indices of the k weakest rows in the matrix ordered from weakest to strongest.

BINARY SEARCH / BUCKET SORT: Use binary search in each row to calculate the strength. Use a 2D associative array (with each row length bounded by the length of the mat rows since that is the maximum possible strength) to map each strength to its row index. Iterate through the associative array to get the first k elements (these will be the k weakest rows). Other approaches: Use binary search to calculate strengths then sort the strengths array. Use binary search to calculate strengths and a priority queue to get the k weakest rows.

Count Negative Numbers in a Sorted Matrix: Given a m x n matrix grid which is sorted in non-increasing order both row-wise and column-wise, return the number of negative numbers in grid.

BINARY SEARCH / MATH: A brute force solution would be O(m * n) time. This could be optimized to O(mlogn) by using binary search on each row. However, we can achieve O(m + n) time by realizing that the negative regions of the matrix will form a "staircase" shape. Starting from the bottom-left corner of the matrix, we can count in the negative numbers in each row. If the current number is positive, we increment col index to keep searching for negative in this row. If the current number is negative, we add len(grid[row]) - col to counter and decrement row index to move up to the next row. If at any point row index is less than 0 or col index is greater than len(grid[row]), then we end the loop. Return counter at the end.

Median of Two Sorted Arrays: Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

BINARY SEARCH: Call 2 times (get kth) and k is about half of (m + n). Every call to get kth can reduce the scale k to its half. So the time complexity is log(m + n).

Sqrt(x): Given a non-negative integer x, return the square root of x rounded down to the nearest integer. The returned integer should be non-negative as well. You must not use any built-in exponent function or operator.

BINARY SEARCH: For x >= 2 the square root is always smaller than x // 2 and larger than 0: 0 < a < x//2. Since a is an integer, the problem goes down to the iteration over the sorted set of integer numbers. Here the binary search enters the scene. If x < 2, return x. Set the left boundary to 2, and the right boundary to x / 2. While left <= right: Take num = (left + right) / 2 as a guess. Compute num * num and compare it with x: If num * num > x, move the right boundary right = pivot -1. Else if num * num < x, move the left boundary left = pivot + 1. Otherwise, num * num == x, the integer square root is here, let's return it. Return right.

Convert Sorted Array to Binary Search Tree: Given an integer array nums where the elements are sorted in ascending order, convert it to a height-balanced binary search tree.

BINARY SEARCH: Implement helper function helper(left, right), which constructs BST from nums elements between indexes left and right: If left > right, then there is no elements available for that subtree. Return None. Otherwise, pick left middle element: p = (left + right) // 2. Initiate the root: root = TreeNode(nums[p]). Compute recursively left and right subtrees: root.left = helper(left, p - 1), root.right = helper(p + 1, right). Return helper(0, len(nums) - 1).

Search Insert Position: Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. You must write an algorithm with O(log n) runtime complexity.

BINARY SEARCH: Initialize the left and right pointers: left = 0, right = n - 1. While left <= right: Compare middle element of the array nums[pivot] to the target value target. If the middle element is the target, return pivot. If target < nums[pivot], continue to search on the left subarray: right = pivot - 1. Else continue to search on the right subarray: left = pivot + 1. Return left.

Find the Distance Value Between Two Arrays: Given two integer arrays arr1 and arr2, and the integer d, return the distance value between the two arrays. The distance value is defined as the number of elements arr1[i] such that there is not any element arr2[j] where | arr1[i]-arr2[j] | <= d.

BINARY SEARCH: Sort arr2. Use a helper function is_valid() for each num in arr1. In this helper function, run binary search: If abs(arr2[mid] - num) <= d: return False. If arr2[mid] > num: r = mid. Else: l = mid + 1. If the loop ends, return True. Count how many is_valid() calls return True.

Check If N and Its Double Exist: Given an array arr of integers, check if there exist two indices i and j such that: i != j 0 <= i, j < len(arr) arr[i] == 2 * arr[j]

BINARY SEARCH: Sort the array. Find the point in the array where the values become positive. For each element from this point to the end, run a binary search on elements to the right to see if the double exists. Do the same thing in reverse (from this point to the front) to check the negative values.

Kth Missing Positive Number: Given an array arr of positive integers sorted in a strictly increasing order, and an integer k. Return the kth positive integer that is missing from this array.

BINARY SEARCH: The number of positive integers which are missing before the arr[idx] is equal to arr[idx] - idx - 1. Choose the pivot index in the middle of the array. If the number of positive integers which are missing before arr[pivot] is less than k - continue to search on the right side of the array. Otherwise, continue to search on the left side.

Next Greatest Letter: You are given an array of characters letters that is sorted in non-decreasing order, and a character target. There are at least two different characters in letters. Return the smallest character in letters that is lexicographically greater than target. If such a character does not exist, return the first character in letters.

BINARY SEARCH: Use binary search to find the rightmost position to insert target into letters so that it remains sorted.

Single Number: Given a non-empty array of integers nums, every element appears twice except for one. Find that single one. You must implement a solution with a linear runtime complexity and use only constant extra space.

BIT MANIPULATION: If we take XOR of zero and some bit, it will return that bit. If we take XOR of two same bits, it will return 0. So we can XOR all bits together to find the unique number.

Balanced Binary Tree: Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary tree is defined as: a binary tree in which the left and right subtrees of every node differ in height by no more than 1.

BOTTOM-UP RECURSION: Keep a top-level result boolean variable (starts off true, will return this at the end). Use a height() helper method. If root == None, return None. Calculate the height of root.left and root.right. If the height of left and right differ by more than 1, set result to false. Return the maximum of the left and right heights + 1.

Add Binary: Given two binary strings a and b, return their sum as a binary string.

CHARACTER MATH: Initialize i = len(a) - 1, j = len(b) - 1, and carry = 0. While i or j is >= 0, set sum = 0 in loop. If i is >= 0, add ord(a[i]) - ord('0') to sum and decrement i. Do the same for j. Add carry to sum. Append sum % 2 to the result string. Set carry to sum / 2. At the end, append another 1 if carry is 1, then reverse the result string and return it.

Moving Average from Data Stream: Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window. Implement the MovingAverage class: MovingAverage(int size) Initializes the object with the size of the window size. double next(int val) Returns the moving average of the last size values of the stream.

DEQUE: First of all, one might notice that we do not need to keep all values from the data stream, but rather the last n values which falls into the moving window. By definition of the moving window, at each step, we add a new element to the window, and at the same time we remove the oldest element from the window. Here, we could apply a data structure called double-ended queue (a.k.a deque) to implement the moving window, which would have the constant time complexity O(1) to add or remove an element from both its ends. Secondly, to calculate the sum, we do not need to reiterate the elements in the moving window. We could keep the sum of the previous moving window, then in order to obtain the sum of the new moving window, we simply add the new element and subtract the oldest element. CIRCULAR QUEUE: The major advantage of circular queue is that by adding a new element to a full circular queue, it automatically discards the oldest element. Unlike deque, we do not need to explicitly remove the oldest element. Another advantage of circular queue is that a single index suffices to keep track of both ends of the queue, unlike deque where we have to keep a pointer for each end. No need to resort to any library, one could easily implement a circular queue with a fixed-size array. The key to the implementation is the correlation between the index of head and tail elements, which we could summarize in the following formula: tail = (head+1) % size. In other words, the tail element is right next to the head element. Once we move the head forward, we would overwrite the previous tail element.

Binary Tree Inorder Traversal: Given the root of a binary tree, return the inorder traversal of its nodes' values.

DFS, STACK: Create a stack and cur node to iterate with (set it to root at the beginning). While cur isn't None or the stack isn't empty, push all of cur.left children onto the stack until cur is None. Then, pop off the stack (setting cur), add this value to the list, and set cur to be cur.right.

Binary Tree Postorder Traversal: Given the root of a binary tree, return the postorder traversal of its nodes' values.

DFS, STACK: Create a stack and push the root node onto it. While the stack isn't empty, pop the top node and check if it is None. If it isn't None, add the node value to the front of the list (use LinkedLists's addFirst() method) and add the node.left and node.right to the stack. Same as preorder but add to front of list rather than the back.

Binary Tree Preorder Traversal: Given the root of a binary tree, return the preorder traversal of its nodes' values.

DFS, STACK: Create a stack and push the root node onto it. While the stack isn't empty, pop the top node and check if it is None. If it isn't None, add the node value to the list and add the node.right and node.left (in that order) to the stack.

Flatten Multilevel Doubly Linked List: You are given a doubly linked list, which contains nodes that have a next pointer, a previous pointer, and an additional child pointer. This child pointer may or may not point to a separate doubly linked list, also containing these special nodes. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure as shown in the example below. Given the head of the first level of the list, flatten the list so that all the nodes appear in a single-level, doubly linked list. Let curr be a node with a child list. The nodes in the child list should appear after curr and before curr.next in the flattened list. Return the head of the flattened list. The nodes in the list must have all of their child pointers set to null.

DFS: 1. Start from the head, move one step each time to the next node. 2. If no child, proceed. When met with a node with a child, say node p, follow its child chain to the end and connect the tail node with p.next, by doing this we merged the child chain back to the main thread (also setting p.next.prev to the tail of the child chain). 3. Return to p (setting p.next to p.child, p.child.prev to p, and p.child to None) and proceed until find next node with child. 4. Repeat until reach None.

Find a Corresponding Node of a Binary Tree in a Clone of That Tree: Given two binary trees original and cloned and given a reference to a node target in the original tree. The cloned tree is a copy of the original tree. Return a reference to the same node in the cloned tree. Note that you are not allowed to change any of the two trees or the target node and the answer must be a reference to a node in the cloned tree.

DFS: Base case: if original node is None or equals target, return None or cloned, respectively. Otherwise, recurse to the left and right subtrees and return whichever isn't None.

Deepest Leaves Sum: Given the root of a binary tree, return the sum of values of its deepest leaves.

DFS: First, find the maximum depth of the tree. Then, sum all of the values for nodes that are at the deepest depth. Two separate recursive helper functions.

Counting Bits: Given an integer n, return an array ans of length n + 1 such that for each i (0 <= i <= n), ans[i] is the number of 1's in the binary representation of i.

DYNAMIC PROGRAMMING: Create a dp array of size n + 1 and initialize to 0. Keep track of an offset which is the current power of 2 (start at 1). Iterate through numbers 1...n and check if current index is a new power of 2 (offset * 2 == i). If it is, set the new offset to be i and set the value of dp[i] = 1 (powers of 2 have a single 1 bit). Otherwise, set dp[i] = 1 + dp[i - offset].

Count Univalue Subtrees: Given the root of a binary tree, return the number of uni-value subtrees. A uni-value subtree means all nodes of the subtree have the same value.

DFS: Given a node in our tree, we know that it is a univalue subtree if it meets one of the following criteria: - The node has no children (base case) - All of the node's children are univalue subtrees, and the node and its children all have the same value With this in mind we can perform a depth-first-search on our tree, and test if each subtree is uni-value in a bottom-up manner. 1. Base case - if the node has no children this is a univalue subtree. Found a univalue subtree so increment count. 2. Initialize is_uni = True. This will be set by the recursive calls. 3. Check if all of the node's children are univalue subtrees and if they have the same value: isUnival(root.left) and is_uni and root.left.val == root.val. Do the same for root.right. The intersection of all of this becomes the value for is_uni. 4. Increment self.count by is_uni and return whether a univalue tree exists here (in other words, return is_uni).

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.

DFS: If root is None, return 0. Otherwise, return the max of the left and right recursive calls plus 1.

All Paths From Source To Target: Given a directed acyclic graph (DAG) of n nodes labeled from 0 to n - 1, find all possible paths from node 0 to node n - 1 and return them in any order. The graph is given as follows: graph[i] is a list of all nodes you can visit from node i (i.e., there is a directed edge from node i to node graph[i][j]).

DFS: Let's save each path in a list called result. When we reach the target, we need to use .copy() to move this path to final result. Here our start = 0, end = len(graph)-1. If we didn't reach the end, then traverse through each node that can be reached from the current node. Add the current node to the results, traverse from this node and when you return, backtrack the added node.

Clone Tree: 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.

DFS: Start traversing the graph from the given node. We can use a hash map to store the reference of the copy of all the nodes that have already been visited and cloned. The key for the hash map would be the node of the original graph and corresponding value would be the corresponding cloned node of the cloned graph. If the node already exists in the visited we return corresponding stored reference of the cloned node. For a given edge A - B, since A is connected to B and B is also connected to A if we don't use visited we will get stuck in a cycle. If we don't find the node in the visited hash map, we create a copy of it and put it in the hash map. Note, how it's important to create a copy of the node and add to the hash map before entering recursion. Now make the recursive call for the neighbors of the node. Pay attention to how many recursion calls we will be making for any given node. For a given node the number of recursive calls would be equal to the number of its neighbors. Each recursive call made would return the clone of a neighbor. We will prepare the list of these clones returned and put into neighbors of the cloned (current) node which we had created earlier. This way we will have cloned the given node and it's neighbors.

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.

DFS: Use a HashMap to map numbers to letters. Construct all possible combinations using DFS and recursive helper function. If combination is of correct length, add it to the set. As mentioned previously, we need to lock-in letters when we generate new letters. The easiest way to save state like this is to use recursion. Our algorithm will be as follows: If the input is empty, return an empty array. Initialize a data structure (e.g. a hash map) that maps digits to their letters. Use a backtracking function to generate all possible combinations. The function should take 2 primary inputs: the current combination of letters we have, path, and the index we are currently checking. As a base case, if our current combination of letters is the same length as the input digits, that means we have a complete combination. Therefore, add it to our answer, and return (begin backtrack). Otherwise, get all the letters that correspond with the current digit we are looking at, digits[index]. Loop through these letters. For each letter, add the letter to our current path, and call backtrack again, but move on to the next digit by incrementing index by 1. Make sure to remove the letter from path once finished with it.

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.

DUMMY NODE: Use a dummy head at the front, keep track of carry variable, use basic arithmetic rules, remember to add in final carry at the end, return dummyHead.next.

Trapping Rain Water: Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.

DYNAMIC PROGRAMMING: Find maximum height of bar from the left end up to an index i in the array left_max. Find maximum height of bar from the right end up to an index i in the array right_max. Iterate over the height array and update ans: add min⁡(left_max[i], right_max[i])−height[i] to ans

Range Sum Query 2D - Immutable: Given a 2D matrix matrix, handle multiple queries of the following type: Calculate the sum of the elements of matrix inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). Implement the NumMatrix class: NumMatrix(int[][] matrix) Initializes the object with the integer matrix matrix. int sumRegion(int row1, int col1, int row2, int col2) Returns the sum of the elements of matrix inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). You must design an algorithm where sumRegion works on O(1) time complexity.

DYNAMIC PROGRAMMING: We used a cumulative sum array in the 1D version. We notice that the cumulative sum is computed with respect to the origin at index 0. Extending this analogy to the 2D case, we could pre-compute a cumulative region sum with respect to the origin at (0,0). Sum(ABCD)=Sum(OD)−Sum(OB)−Sum(OC)+Sum(OA). OB is rectangle strictly above. OC is the rectangle strictly to the left. OA is the rectangle strictly to the top left (double subtracted out so we need to add it back in).

Longest Palindromic Substring: Given a string s, return the longest palindromic substring in s. A string is called a palindrome string if the reverse of that string is the same as the original string.

DYNAMIC PROGRAMMING: When you increase s by 1 character, you could only increase maxPalindromeLen by 1 or 2, and that new maxPalindrome includes this new character. Proof: if after adding 1 character, maxPalindromeLen increased by 3 or more, say the new maxPalindromeLen is Q, and the old maxPalindromeLen is P, and Q >= P+3. Then it would mean, even without this new character, there would be a palindromic substring ending in the last character, whose length is at least Q-2. Since Q-2 would be > P, this contradicts the condition that P is the maxPalindromeLen without the additional character. So, it becomes simple, you only need to scan from beginning to the end, adding one character at a time, keeping track of maxPalindromeLen, and for each added character, you check if the substrings ending with this new character, with length P+1 or P+2, are palindromes, and update accordingly.

Palindrome Linked List: Given the head of a singly linked list, return true if it is a palindrome.

FAST AND SLOW POINTER, STACK: Fast pointer goes two at a time, slow pointer goes one at a time and adds to stack. When fast pointer gets to the end, slow pointer will be at the middle. Set fast pointer to head and begin popping off stack to make sure elements all match. ---------------------------------------------------------------------- FAST AND SLOW POINTER, REVERSE: Same fast and slow pointer method as above, but when fast becomes None, set it to the beginning and reverse the second half of the linked list. Then compare all the elements until the end.

Linked List Cycle II: Given the head of a linked list, return the node where the cycle begins. If there is no cycle, return null. 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 (0-indexed). It is -1 if there is no cycle. Note that pos is not passed as a parameter. Do not modify the linked list.

FAST AND SLOW POINTER: Use the same fast and slow pointer strategy with Linked List Cycle I. However, when a cycle is found, create a new pointer pointing to the head of the list. Increment slow and this new pointer until they are equal. This is the point where the cycle begins. Return this node.

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.

FAST AND SLOW POINTERS: Use two pointers. Start the right pointer n + 1 ahead of the left pointer. Then, when the right is at the end, skip over the node to be deleted using the left.next.next access. For edge case with head, create a dummy node which has a next pointer to head. Then at the end, return dummy.next.

Integer to Roman: Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II. Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: I can be placed before V (5) and X (10) to make 4 and 9. X can be placed before L (50) and C (100) to make 40 and 90. C can be placed before D (500) and M (1000) to make 400 and 900. Given an integer, convert it to a roman numeral.

GREEDY: As explained in the overview, the representation should use the largest possible symbols, working from the left (store all possible values including the subtraction instances). Therefore, it makes sense to use a Greedy algorithm. A Greedy algorithm is an algorithm that makes the best possible decision at the current time; in this case taking out the largest possible symbol it can. So to represent a given integer, we look for the largest symbol that fits into it. We subtract that, and then look for the largest symbol that fits into the remainder, and so on until the remainder is 0. Each of the symbols we take out are appended onto the output Roman Numeral string.

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.

HASH MAP: The idea behind this approach is as follows: If the cumulative sum (represented by sum[i] for sum up to ith index) up to two indices is the same, the sum of the elements lying in between those indices is zero. Extending the same thought further, if the cumulative sum up to two indices, say i and j is at a difference of k i.e. if sum[i]−sum[j]=k, the sum of elements lying between indices i and j is k. Based on these thoughts, we make use of a hash map, map, which is used to store the cumulative sum up to all the indices possible along with the number of times the same sum occurs. We store the data in the form (sum_i, no. of occurrences of sum_i). We traverse over the array nums and keep on finding the cumulative sum. Every time we encounter a new sum, we make a new entry in the map corresponding to that sum. If the same sum occurs again, we increment the count corresponding to that sum in the map. Further, for every sum encountered, we also determine the number of times the sum sum−k has occurred already, since it will determine the number of times a subarray with sum k has occurred up to the current index. We increment the count by the same amount. After the complete array has been traversed, the count gives the required result. The animation below depicts the process.

Two Sum IV - Input is a BST: Given the root of a binary search tree and an integer k, return true if there exist two elements in the BST such that their sum is equal to k, or false otherwise.

HASHING / BST: We keep a track of the elements which have been found so far during the tree traversal, by putting them into a set. For every current node with a value of p, we check if k−p already exists in the set. If so, we can conclude that the sum k can be formed by using the two elements from the given tree. Otherwise, we put this value p into the set. If even after the whole tree's traversal, no such element p can be found, the sum k can't be formed by using any two elements.

Insert Delete GetRandom O(1): Implement the RandomizedSet class: RandomizedSet() Initializes the RandomizedSet object. bool insert(int val) Inserts an item val into the set if not present. Returns true if the item was not present, false otherwise. bool remove(int val) Removes an item val from the set if present. Returns true if the item was present, false otherwise. int getRandom() Returns a random element from the current set of elements (it's guaranteed that at least one element exists when this method is called). Each element must have the same probability of being returned. You must implement the functions of the class such that each function works in average O(1) time complexity.

HASHING, ARRAY LIST: Insert: Add (value, index) key-value pair into dictionary, average O(1) time. Append value to array list, average O(1) time as well. Delete: Retrieve the index of the element to delete from the dictionary. Move the last element to the place of the element to delete, O(1) time. Pop the last element out, O(1) time. GetRandom: GetRandom could be implemented in O(1) time with the help of standard random.choice(list) in Python.

Longest Palindrome by Concatenating Two Letter Words: You are given an array of strings words. Each element of words consists of two lowercase English letters. Create the longest possible palindrome by selecting some elements from words and concatenating them in any order. Each element can be selected at most once. Return the length of the longest palindrome that you can create. If it is impossible to create any palindrome, return 0. A palindrome is a string that reads the same forward and backward.

HASHING: Count the number of occurrences of each word using a hashmap (can use a Counter in Python and pass in the list to initialize). Initialize answer=0, central=false. The answer will denote the number of words in the final string and the boolean variable central will denote whether we have a central word. For each palindromic word do the following. If count[word] is even, increase answer by count[word]. Otherwise, if count[word] is odd, increase answer by count[word]−1 and set central=true (we can use the word as a central word). For each non-palindrome word such that word[0]<word[1] (we need this condition to consider each pair only once and not twice, e.g. we don't want to consider both ba and ab separately) increase answer by 2⋅min⁡(count[word],count[reversedWord]) (we use min⁡(count[word],count[reversedWord]) pairs of the corresponding words). If central=true, increase answer by 1.Return 2⋅answer. (Because each word has a length of 2).

Word Pattern: Given a pattern and a string s, find if s follows the same pattern. Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in s.

HASHING: Split the string into an array of words. Verify the length matches the length of the pattern. Construct a HashMap mapping the string to the pattern. Four cases: (1) word in keys and pattern not in values (2) word not in keys and pattern in values (3) both in the hash map but don't match mapping (4) neither in the hash map.

First Unique Character in a String: Given a string s, find the first non-repeating character in it and return its index. If it does not exist, return -1.

HASHING: The best possible solution here could be of a linear time because to ensure that the character is unique you have to check the whole string anyway. The idea is to go through the string and save in a HashMap the number of times each character appears in the string. And then we go through the string the second time, this time we use the HashMap as a reference to check if a character is unique or not.

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.

HEAP: In the constructor, create a min heap using the elements from nums (heapq.heapify(nums)). Then, pop from the heap until heap.length == k (heapq.heappop(self.heap)). For every call to add(): First, push val into heap (heapq.heappush(self.heap, val)).Next, check if heap.length > k. If so, pop from the heap (heapq.heappop(self.heap)). Finally, return the smallest value from the heap, which we can get in O(1) time (self.heap[0]).

Valid Anagram: Given two strings s and t, return true if t is an anagram of s, and false otherwise. 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.

HASHING: To examine if t is a rearrangement of s, we can count occurrences of each letter in the two strings and compare them. We could use a HashMap to count the frequency of each letter, however, since both s and t only contain letters from a to z, a simple array of size 26 will suffice. Do we need two counters for comparison? Actually no, because we can increment the count for each letter in s and decrement the count for each letter in t, and then check if the count for every character is zero. Or we could first increment the counter for s, then decrement the counter for t. If at any point the counter drops below zero, we know that t contains an extra letter not in s and return false immediately.

Isomorphic Strings: Given two strings s and t, determine if they are isomorphic. Two strings s and t are isomorphic if the characters in s can be replaced to get t. All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character, but a character may map to itself.

HASHING: Use a HashMap and a HashSet. The HashMap keeps track of the mappings between characters of s and characters of t. The HashSet keeps track of which characters of t have already been assigned (since we cannot map multiple characters of s to the same character of t). If the map contains s[i] and it doesn't equal t[i], return false. If the map doesn't contain s[i] but the set contains t[i], return false. Else, update map and set appropriately.

Copy List with Random Pointer: A linked list of length n is given such that each node contains an additional random pointer, which could point to any node in the list, or null. Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes should point to new nodes in the copied list such that the pointers in the original list and copied list represent the same list state. None of the pointers in the new list should point to nodes in the original list.

HASHING: Use a HashSet to avoid creating duplicate nodes. Iterate through the list, checking if the current node is in the HashSet, creating a new node and adding it if necessary. Set the current node's next and random pointers (retrieving from HashSet or creating and adding).

Longest Palindrome: Given a string s which consists of lowercase or uppercase letters, return the length of the longest palindrome that can be built with those letters. Letters are case sensitive, for example, "Aa" is not considered a palindrome here.

HASHING: Use a HashSet to keep track of the number of pairs of characters (either increment pairs and remove c from set or add c to set). After iterating through all characters, check if s.length > pairs * 2. If so, return pairs * 2 + 1, else return pairs * 2.

Ransom Note: Given two strings ransomNote and magazine, return true if ransomNote can be constructed by using the letters from magazine and false otherwise. Each letter in magazine can only be used once in ransomNote.

HASHING: We can simply put the magazine into a HashMap, and then subtract characters from the ransom note from it, verifying that there is a letter available.

Minimum Absolute Difference in BST: Given the root of a Binary Search Tree (BST), return the minimum absolute difference between the values of any two different nodes in the tree.

INORDER TRAVERSAL: Create a list of integers inorder_nodes to store the node values. Perform the inorder traversal of the binary search tree (BST). Create an integer variable minDifference and initialize it to infinity. Iterate over inorderNodes starting from index 1, and for each element at index i, find the difference with the element at index i - 1 and update the variable minDifference accordingly. Return minDifference.

Meeting Rooms: Given an array of meeting time intervals where intervals[i] = [starti, endi], determine if a person could attend all meetings.

INTERVALS: The idea here is to sort the meetings by starting time. Then, go through the meetings one by one and make sure that each meeting ends before the next one starts.

Kth Smallest Element in a BST: Given the root of a binary search tree, and an integer k, return the kth smallest value (1-indexed) of all the values of the nodes in the tree.

ITERATIVE INORDER TRAVERSAL: It's a very straightforward approach with O(N) time complexity. The idea is to build an inorder traversal of BST which is an array sorted in the ascending order. Now the answer is the k - 1th element of this array. If you do this iteratively, you can stop early once you've seen k elements. The above recursion could be converted into iteration, with the help of stack. This way one could speed up the solution because there is no need to build the entire inorder traversal, and one could stop after the kth element.

Binary Search Tree Iterator: Implement the BSTIterator class that represents an iterator over the in-order traversal of a binary search tree (BST): BSTIterator(TreeNode root) Initializes an object of the BSTIterator class. The root of the BST is given as part of the constructor. The pointer should be initialized to a non-existent number smaller than any element in the BST. boolean hasNext() Returns true if there exists a number in the traversal to the right of the pointer, otherwise returns false. int next() Moves the pointer to the right, then returns the number at the pointer. Notice that by initializing the pointer to a non-existent smallest number, the first call to next() will return the smallest element in the BST. You may assume that next() calls will always be valid. That is, there will be at least a next number in the in-order traversal when next() is called.

ITERATIVE INORDER TRAVERSAL: Trivial solution is to construct an inorder traversal array (recursively) and just iterate over it. An optimized solution uses controlled recursion with a stack. Initialize an empty stack S which will be used to simulate the inorder traversal for our binary search tree. Note that we will be following the same approach for inorder traversal as before except that now we will be using our own stack rather than the system stack. Since we are using a custom data structure, we can pause and resume the recursion at will. Let's also consider a helper function that we will be calling again and again in the implementation. This function, called inorder_left will essentially add all the nodes in the leftmost branch of the tree rooted at the given node root to the stack and it will keep on doing so until there is no left child of the root node. For a given node root, the next smallest element will always be the leftmost element in its tree. So, for a given root node, we keep on following the leftmost branch until we reach a node which doesn't have a left child and that will be the next smallest element. The first time next() function call is made, the smallest element of the BST has to be returned and then our simulated recursion has to move one step forward i.e. move onto the next smallest element in the BST. The invariant that will be maintained in this algorithm is that the stack top always contains the element to be returned for the next() function call. However, there is additional work that needs to be done to maintain that invariant. It's very easy to implement the hasNext() function since all we need to check is if the stack is empty or not. So, we will only focus on the next() call from now. Initially, given the root node of the BST, we call the function inorder_left and that ensures our invariant holds. Let's see this first step with an example. Suppose we get a call to the next() function. The node which we have to return i.e. the next smallest element in the binary search tree iterator is the one sitting at the top of our stack. Now, there are two possibilities that we have to deal with: One is where the node at the top of the stack is actually a leaf node. This is the bes

Add to Array-Form Integer: The array-form of an integer num is an array representing its digits in left to right order.For example, for num = 1321, the array form is [1,3,2,1]. Given num, the array-form of an integer, and an integer k, return the array-form of the integer num + k.

MATH: Use schoolbook addition technique of divisor and mod to update the array in-place. At the end, keep prepending the k % 10 to the num array and dividing k by 10 until it is 0.

Partitioning Into Minimum Number Of Deci-Binary Numbers: A decimal number is called deci-binary if each of its digits is either 0 or 1 without any leading zeros. For example, 101 and 1100 are deci-binary, while 112 and 3001 are not. Given a string n that represents a positive decimal integer, return the minimum number of positive deci-binary numbers needed so that they sum up to n.

MATH: Assume max digit in n is x. Because deci-binary only contains 0 and 1, we need at least x numbers to sum up a digit x. Iterate through characters in String n, find numeric value by subtracting '0' from characters (ASCII math). If digit is greater than max, set max to digit.

Ugly Number: An ugly number is a positive integer whose prime factors are limited to 2, 3, and 5. Given an integer n, return true if n is an ugly number.

MATH: Continuously divide the number by 2, 3, or 5 (depending on which one has a modulo == 0) and then return true if the number ever reaches 1. If the number reaches 0 (or less) or none of the modulo operators equal 0 anymore, return false.

Sparse Matrix Multiplication: Given two sparse matrices mat1 of size m x k and mat2 of size k x n, return the result of mat1 x mat2. You may assume that multiplication is always possible.

MATH: Create a function compressMatrix(matrix), which inputs matrix and returns compressedMatrix with only non-zero elements. To build compressedMatrix we iterate over each element of matrix and if the element is non-zero push the (value, col) pair in the respective row of compressedMatrix. Initialize some variables: m, number of rows in mat1. k, number of columns in mat1. n, number of columns in mat2. A and B, data structure to store matrices mat1 and mat2 in compressed form. ans, matrix of size m×n to store multiplication result. For each row in A, iterate over all its elements. These represent the non-zero elements from mat1. For each element, we get (value, col) pair and iterate over all the elements of colth row in B. For each pair of elements, we add their product to the ans matrix. Return the ans matrix.

Excel Sheet Column Title: Given an integer columnNumber, return its corresponding column title as it appears in an Excel sheet.

MATH: Create an array (capitals) of size 26 representing A...Z. We can use (num-1)%26 instead, then we get a number range from 0 to 25. While num > 0, append chr((num-1)%26) + ord('A')). Then num = (num - 1) / 26.

Subtract the Product and Sum of Digits of an Integer: Given an integer number n, return the difference between the product of its digits and the sum of its digits.

MATH: Get the current digit using n % 10. Shift to the next digit using n /= 10;

Valid Perfect Square: Given a positive integer num, return true if num is a perfect square or false otherwise. A perfect square is an integer that is the square of an integer. In other words, it is the product of some integer with itself. You must not use any built-in library function, such as sqrt.

MATH: Math trick for square number is 1+3+5+ ... +(2n-1). Check all numbers less than num.

Plus One: You are given a large integer represented as an integer array digits, where each digits[i] is the ith digit of the integer. The digits are ordered from most significant to least significant in left-to-right order. The large integer does not contain any leading 0's. Increment the large integer by one and return the resulting array of digits.

MATH: Move along the input array starting from the end of array. Set all the nines at the end of array to zero. If we meet a not-nine digit, we would increase it by one. The job is done: return digits. Otherwise, we're here because all the digits were equal to nine. Now they have all been set to zero. We then append the digit 1 in front of the other digits and return the result.

Convert Binary Number in a Linked List to Integer: Given head which is a reference node to a singly-linked list. The value of each node in the linked list is either 0 or 1. The linked list holds the binary representation of a number. Return the decimal value of the number in the linked list. The most significant bit is at the head of the linked list.

MATH: Results starts off as head.val. While head.next != None, num = num * 2 + head.next.val. Then, head = head.next.

Multiply Strings: Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string. Note: You must not use any built-in BigInteger library or convert the inputs to integer directly.

MATH: Reverse both numbers. For each digit in secondNumber: Keep a carry variable, initially equal to 0. Initialize currentResult array beginning with the appropriate number of zeros according to the place of the secondNumber digit. For each digit in firstNumber: Multiply the secondNumber's digit and the firstNumber's digit and add carry to the multiplication. Take the remainder of multiplication with 10 to get the last digit. Append the last digit to the currentResult. Divide multiplication by 10 to get the new value for carry. Append the remaining value for carry (if any) to the currentResult. Push the currentResult into the results array. Compute the cumulative sum over all the obtained arrays using the ans as an answer. Reverse ans and return it.

Add Strings: Given two non-negative integers, num1 and num2 represented as string, return the sum of num1 and num2 as a string. You must solve the problem without using any built-in library for handling large integers (such as BigInteger). You must also not convert the inputs to integers directly.

MATH: Reverse both of the strings. Find the length of both strings. If not at the end of string, add get the digit value using ord(num[i])-ord('0'). The next sum digit is (digit1 + digit2 + carry) % 10. The carry becomes (digit1 + digit2 + carry) // 10. Append the carry at the end if greater than 0. Finally, reverse the result array and join it into a string.

Find the Difference: You are given two strings s and t. String t is generated by random shuffling string s and then add one more letter at a random position. Return the letter that was added to t.

MATH: Set charCode to be the integer value of the last character of t (which is one larger than s). Then, iterate through s, subtracting the char value of s from charCode and adding the char value of t to charCode. Then, return charCode cast as a character.

Factorial Trailing Zeros: Given an integer n, return the number of trailing zeroes in n!. Note that n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1.

MATH: The number of trailing zeroes is equal to the number of times 10 is a factor. But since there are many more 2's than there are 5's (from the even factors), we just need to find how many times 5 is a factor. n /= 5, count += n.

Delete Node in a Linked List: Write a function to delete a node in a singly-linked list. You will not be given access to the head of the list, instead you will be given access to the node to be deleted directly. It is guaranteed that the node to be deleted is not a tail node in the list.

POINTERS: Copy the node's next value to the node and set the node's next node to be the node.next.next.

Remove Linked List Elements: Given the head of a linked list and an integer val, remove all the nodes of the linked list that has Node.val == val, and return the new head.

POINTERS: Create a dummy node with value 0 that points to head; this becomes the new head. Create a cur pointer that starts at this new head and iterates while cur != None and cur.next != None. If cur.next.val == val, skip over this node. Finally, return head.next.

Reverse Linked List: Given the head of a singly linked list, reverse the list, and return the reversed list.

POINTERS: Keep track of prev, cur, next. Initialize cur = head, prev = None, next = cur.next. While cur != None: next = cur.next, cur.next = prev, prev = cur, cur = next.

Flatten Binary Tree to Linked List: Given the root of a binary tree, flatten the tree into a "linked list": - The "linked list" should use the same TreeNode class where the right child pointer points to the next node in the list and the left child pointer is always null. - The "linked list" should be in the same order as a pre-order traversal of the binary tree.

POST-ORDER TRAVERSAL: Trivial: Run pre-order traversal and fill a list. Convert list to a LinkedList. Constant space: use a recursive post-order traversal to update nodes in place. Helper function takes in TreeNode root and TreeNode prev. First set root = helper(root, None). In the helper, if root == None, return prev. prev = helper(root.right, prev) then prev = helper(root.left, prev). Set root.right = prev and set root.left = None. Return root.

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

PREFIX SUM: Let's initialize tree paths counter count = 0, and the hashmap h "prefix sum -> how many times was it seen so far". One could parse the tree using recursive preorder traversal: node -> left -> right: preorder(node: TreeNode, curr_sum: int) -> None. This function takes two arguments: a tree node and a prefix sum before that node. To start the recursion chain, one should call preorder(root, 0). The first thing is to update the current prefix sum by adding the value of the current node: curr_sum += node.val. Now one could update the counter. One should consider two situations. In situation 1, the tree path with the target sum starts from the root. That means the current prefix sum is equal to the target sum curr_sum == k, so one should increase the counter by 1: count += 1. In situation 2, the tree path with the target sum starts somewhere downwards. That means we should add to the counter the number of times we have seen the prefix sum curr_sum - target so far: count += h[curr_sum - target]. The logic is simple: the current prefix sum is curr_sum, and several elements before the prefix sum was curr_sum - target. All the elements in between sum up to curr_sum - (curr_sum - target) = target. Now it's time to update the hashmap: h[curr_sum] += 1. Let's parse left and right subtrees: preorder(node.left, curr_sum), preorder(node.right, curr_sum). Now the current subtree is processed. It's time to remove the current prefix sum from the hashmap, in order not to blend the parallel subtrees: h[curr_sum] -= 1. Now the preorder traversal is done, and the counter is updated. Return it.

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.

PREFIX TREE: Trie is a rooted tree. Its nodes have the following fields: - Maximum of R links to its children, where each link corresponds to one of R character values from dataset alphabet. In this article we assume that R is 26, the number of lowercase latin letters. - Boolean field which specifies whether the node corresponds to the end of the key, or is just a key prefix. INSERT: We insert a key by searching into the trie. We start from the root and search a link, which corresponds to the first key character. There are two cases: 1. A link exists. Then we move down the tree following the link to the next child level. The algorithm continues with searching for the next key character. 2. A link does not exist. Then we create a new node and link it with the parent's link matching the current key character. We repeat this step until we encounter the last character of the key, then we mark the current node as an end node and the algorithm finishes. SEARCH: Each key is represented in the trie as a path from the root to the internal node or leaf. We start from the root with the first key character. We examine the current node for a link corresponding to the key character. There are two cases: 1. A link exist. We move to the next node in the path following this link, and proceed searching for the next key character. 2. A link does not exist. If there are no available key characters and current node is marked as isEnd we return true. Otherwise there are possible two cases in each of them we return false: 1. There are key characters left, but it is impossible to follow the key path in the trie, and the key is missing. 2. No key characters left, but current node is not marked as isEnd. Therefore the search key is only a prefix of another key in the trie.

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.

PREFIX/POSTFIX PRODUCT (TWO ARRAYS) Instead of dividing the product of all the numbers in the array by the number at a given index to get the corresponding product, we can make use of the product of all the numbers to the left and all the numbers to the right of the index. Multiplying these two individual products would give us the desired result as well. For every given index, i, we will make use of the product of all the numbers to the left of it and multiply it by the product of all the numbers to the right. This will give us the product of all the numbers except the one at the given index i. Let's look at a formal algorithm describing this idea more concretely. Initialize two empty arrays, L and R where for a given index i, L[i] would contain the product of all the numbers to the left of i and R[i] would contain the product of all the numbers to the right of i. We would need two different loops to fill in values for the two arrays. For the array L, L[0] would be 1 since there are no elements to the left of the first element. For the rest of the elements, we simply use L[i]=L[i−1]∗nums[i−1]. Remember that L[i] represents product of all the elements to the left of element at index i. For the other array, we do the same thing but in reverse i.e. we start with the initial value of 1 in R[length−1] where length is the number of elements in the array, and keep updating R[i] in reverse. Essentially, R[i]=R[i+1]∗nums[i+1]. Remember that R[i] represents product of all the elements to the right of element at index i. Once we have the two arrays set up properly, we simply iterate over the input array one element at a time, and for each element at index i, we find the product except self as L[i]∗R[i]. PREFIX/POSTFIX PRODUCT (ONE ARRAY): This approach can be optimized by using only one array and doing a two pass over the original array. On the first pass, keep track of a prefix variable and for each i, insert the prefix up to that position i and then multiply the prefix by nums[i]. On the second pass, keep track of a postfix variable and iterate backwards, inserting the postfix of i at position i and then multiplying by nums[i].

Leaf-Similar Tree: Consider all the leaves of a binary tree, from left to right order, the values of those leaves form a leaf value sequence. Two binary trees are considered leaf-similar if their leaf value sequence is the same. Return true if and only if the two given trees with head nodes root1 and root2 are leaf-similar.

PREORDER TRAVERSAL / DFS: Let's find the leaf value sequence for both given trees. Afterwards, we can compare them to see if they are equal or not. To find the leaf value sequence of a tree, we use a depth first search. Our dfs function writes the node's value if it is a leaf, and then recursively explores each child. This is guaranteed to visit each leaf in left-to-right order, as left-children are fully explored before right-children.

Top K Frequent Words: Given an array of strings words and an integer k, return the k most frequent strings. Return the answer sorted by the frequency from highest to lowest. Sort the words with the same frequency by their lexicographical order.

PRIORITY QUEUE: Create a HashMap containing the frequencies of each word. Create a heapq of the words using heapq.heapify(list). Now get the top k most frequent using heapq.heappop(heap). Since heapq is a min heap, we can simulate a max heap by using negative values for the frequencies. ---------------------------------------------------------------------- BUCKET SORT: Add the words to another list using the frequencies as indices. Then, iterate backwards from the list to construct the result.

Last Stone Weight: You are given an array of integers stones where stones[i] is the weight of the ith stone. We are playing a game with the stones. On each turn, we choose the heaviest two stones and smash them together. Suppose the heaviest two stones have weights x and y with 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.

PRIORITY QUEUE: Create a new heapq (min heap): heapq.heapify(list). Poll from the front and add back to the heap if necessary. Check conditions for returning.

Implement Stack using Queues: Implement a last-in-first-out (LIFO) stack using only two queues. The implemented stack should support all the functions of a normal stack (push, top, pop, and empty). Implement the MyStack class: void push(int x) Pushes element x to the top of the stack. int pop() Removes the element on the top of the stack and returns it. int top() Returns the element on the top of the stack. boolean empty() Returns true if the stack is empty, false otherwise.

QUEUE: On push, rotate all of the elements in the queue so the newly pushed element becomes the first one (keep popping left and appending to the other queue until the pushed element is first). Now, on pop or peek you can simply queue.popleft() or queue[0], respectively in O(1) time.

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

QUICK SELECT: We want an algorithm faster than O(nlogn). Clearly, the only way to do this is to use the fact that the K elements returned can be in any order (otherwise we would be sorting which is at least O(nlogn)). Say we choose some random element x = A[i] and split the array into two buckets: one bucket of all the elements less than x and another bucket of all the elements greater than or equal to x. This is known as "quickselecting by a pivot x". The idea is that if we quickselect by some pivot, on average in linear time we'll reduce the problem to a problem of half the size. Let's do the work(i, j, K) of partially sorting the subarray (points[i], points[i+1], ..., points[j]) so that the smallest K elements of this subarray occur in the first K positions (i, i+1, ..., i+K-1). First, we quickselect by a random pivot element from the subarray. To do this in place, we have two pointers i and j, and move these pointers to the elements that are in the wrong bucket—then, we swap these elements. After, we have two buckets [oi, i] and [i+1, oj], where (oi, oj) are the original (i, j) values when calling work(i, j, K). Say the first bucket has 10 items and the second bucket has 15 items. If we were trying to partially sort say, K = 5 items, then we only need to partially sort the first bucket: work(oi, i, 5). Otherwise, if we were trying to partially sort say, K = 17 items, then the first 10 items are already partially sorted, and we only need to partially sort the next 7 items: work(i+1, oj, 7).

Diameter of Binary Tree: Given the root of a binary tree, return 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. The length of a path between two nodes is represented by the number of edges between them.

RECURSION: Initialize an integer variable diameter to keep track of the longest path we find from the DFS. Implement a recursive function longestPath which takes a TreeNode as input. It should recursively explore the entire tree rooted at the given node. Once it's finished, it should return the longest path out of its left and right branches: If node is None, we have reached the end of the tree, hence we should return 0. Otherwise, we want to recursively explore node's children, so we call longestPath again with node's left and right children. In return, we get the longest path of its left and right children leftPath and rightPath. If leftPath plus rightPath is longer than the current longest diameter found, then we need to update diameter. Finally, we return the longer one of leftPath and rightPath. Remember to add 1 to include the edge connecting it with its parent. Begin by calling longestPath with root.

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.

RECURSION, TRAVERSAL: Preorder traversal follows Root -> Left -> Right, therefore, given the preorder array preorder, we have easy access to the root which is preorder[0]. Inorder traversal follows Left -> Root -> Right, therefore if we know the position of Root, we can recursively split the entire array into two subtrees. Now the idea should be clear enough. We will design a recursion function: it will set the first element of preorder as the root, and then construct the entire tree. To find the left and right subtrees, it will look for the root in inorder, so that everything on the left should be the left subtree, and everything on the right should be the right subtree. Both subtrees can be constructed by making another recursion call. It is worth noting that, while we recursively construct the subtrees, we should choose the next element in preorder to initialize as the new roots. This is because the current one has already been initialized to a parent node for the subtrees. Build a hashmap to record the relation of value -> index for inorder, so that we can find the position of root in constant time. Initialize an integer variable preorderIndex to keep track of the element that will be used to construct the root. Implement the recursion function arrayToTree which takes a range of inorder and returns the constructed binary tree: if the range is empty, return None; initialize the root with preorder[preorderIndex] and then increment preorderIndex; recursively use the left and right portions of inorder to construct the left and right subtrees. Simply call the recursion function with the entire range of inorder.

N-ary Tree Preorder Traversal: Given the root of an n-ary tree, return the preorder traversal of its nodes' values.

RECURSION: Add root val to list, recurse to all of the children. ---------------------------------------------------------------------- STACK: Use a stack and add children (a list of nodes) in the reverse order to the stack.

Sum of Left Leaves: Given the root of a binary tree, return the sum of all left leaves. A leaf is a node with no children. A left leaf is a leaf that is the left child of another node.

RECURSION: Base case if root == None, return 0. If root.left == None, add the result of recursing to root.right. Otherwise, if root.left is a leaf (root.left.left and root.left.right are None), add the val to answer. If it is not a leaf, recurse down to root.left.

Symmetric Tree: Given the root of a binary tree, check whether it is a mirror of itself (i.e., symmetric around its center).

RECURSION: Create a helper function which takes in a left and right TreeNode. If either one is None, return if both are None. Else if the values aren't equal, return false. Otherwise, return the helper(left.left, right.right) && helper(left.right, right.left). Start off this recursion from the main function by passing in root.left and root.right (as long as root isn't None).

Delete Node in a BST: Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST. Basically, the deletion can be divided into two stages: Search for a node to remove. If the node is found, delete the node.

RECURSION: If key > root.val then the node to delete is in the right subtree root.right = deleteNode(root.right, key). If key < root.val then the node to delete is in the left subtree root.left = deleteNode(root.left, key). If key == root.val then the node to delete is right here. If the node is a leaf, the delete process is straightforward: root = None. If the node is not a leaf and has the right child, then replace the node value by a successor value root.val = successor.val, and then recursively delete the successor in the right subtree root.right = deleteNode(root.right, root.val). If the node is not a leaf and has only the left child, then replace the node value by a predecessor value root.val = predecessor.val, and then recursively delete the predecessor in the left subtree root.left = deleteNode(root.left, root.val). Return root.

Maximum Binary Tree: You are given an integer array nums with no duplicates. A maximum binary tree can be built recursively from nums using the following algorithm: - Create a root node whose value is the maximum value in nums. - Recursively build the left subtree on the subarray prefix to the left of the maximum value. - Recursively build the right subtree on the subarray suffix to the right of the maximum value. Return the maximum binary tree built from nums.

RECURSION: If nums is empty return None. Find the max value/index in nums. Create a new node with this max value. Set the left to be the result of the recursive call using the left subarray. Set the right to be the result of the recursive call using the right subarray. Return the node.

Search in a Binary Search Tree: You are given the root of a binary search tree (BST) and an integer val. Find the node in the BST that the node's value equals val and return the subtree rooted with that node. If such a node does not exist, return null.

RECURSION: If root is None, return None. If root.val is val, return root. Otherwise recurse left or right depending on comparison.

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)."

RECURSION: If the root val is greater than both p and q nodes, return the lowestCommonAncestor of root.left (recursive call). Likewise, if the root val is less than both p and q nodes, return the lowestCommonAncestor of root.right (recursive call). Otherwise, root is the lowest common ancestor (p and q are on opposite sides of root node), so return root.

Unique Binary Search Trees II: Given an integer n, return all the structurally unique BST's (binary search trees), which has exactly n nodes of unique values from 1 to n. Return the answer in any order.

RECURSION: Let's pick up number i out of the sequence 1...n and use it as the root of the current tree. Then there are i - 1 elements available for the construction of the left subtree and n - i elements available for the right subtree. As we already discussed that results in G(i - 1) different left subtrees and G(n - i) different right subtrees, where G is a Catalan number. Now let's repeat the step above for the sequence 1...(i - 1) to construct all left subtrees, and then for the sequence (i + 1)...n to construct all right subtrees. This way we have a root i and two lists for the possible left and right subtrees. The final step is to loop over both lists to link left and right subtrees to the root.

Swap Nodes in Pairs: Given a linked list, swap every two adjacent nodes and return its head. You must solve the problem without modifying the values in the list's nodes (i.e., only nodes themselves may be changed.)

RECURSION: Start the recursion with head node of the original linked list. Every recursion call is responsible for swapping a pair of nodes. Let's represent the two nodes to be swapped by firstNode and secondNode. Next recursion is made by calling the function with head of the next pair of nodes. This call would swap the next two nodes and make further recursive calls if there are nodes left in the linked list. Once we get the pointer to the remaining swapped list from the recursion call, we can swap the firstNode and secondNode i.e. the nodes in the current recursive call and then return the pointer to the secondNode since it will be the new head after swapping.

Path Sum: Given the root of a binary tree and an integer targetSum, return true if the tree has a root-to-leaf path such that adding up all the values along the path equals targetSum. A leaf is a node with no children.

RECURSION: The most intuitive way is to use a recursion here. One is going through the tree by considering at each step the node itself and its children. If node is not a leaf, one calls recursively hasPathSum method for its children with a sum decreased by the current node value. If node is a leaf, one checks if the the current sum is zero, i.e if the initial sum was discovered.

Insert Into a Binary Search Tree: You are given the root node of a binary search tree (BST) and a value to insert into the tree. Return the root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST. Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. You can return any of them.

RECURSION: The recursion implementation is very straightforward: If root is None: return TreeNode(val). If val > root.val, go to insert into the right subtree. If val < root.val, go to insert into the left subtree. Return root.

Flatten Nested List Iterator: You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may also be integers or other lists. Implement an iterator to flatten it. Implement the NestedIterator class: NestedIterator(List<NestedInteger> nestedList) Initializes the iterator with the nested list nestedList. int next() Returns the next integer in the nested list. boolean hasNext() Returns true if there are still some integers in the nested list and false otherwise.

RECURSION: The simplest way of solving this problem is to flatten the entire input list, in the constructor. Then the actual iterator methods can simply work with this flattened list instead of needing to worry about the input structure. This approach splits the coding into two parts: A function that the constructor can call to make a flattened list; next() and hasNext() methods that iterate over a plain list, by keeping track of the current position within it. The first part is best done with recursion (iteration is more complicated). To flatten the list recursively, notice that we can look at the input as a tree. The integers are the leaf nodes, and the order they should be returned is from left to right. Therefore, we can use a recursive depth-first search to flatten 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.

RECURSION: Use a recursive helper function, passing in the root node, min, and max. If the node is None, return true. Otherwise, if the node is less than the min or greater than the max, return false. Else, return the recursive call to both left and right children nodes (replace min and max as necessary based on direction of recursion).

Invert a Binary Tree: Given the root of a binary tree, invert the tree, and return its root.

RECURSION: Use recursive approach. Invert left and right subtrees. Swap left and right subtrees for current node. Base case is when root is none, return none. right = root.right left = root.left root.right = self.invertTree(left) root.left = self.invertTree(right)

Merge Two Trees: You are given two binary trees root1 and root2. Imagine that when you put one of them to cover the other, some nodes of the two trees are overlapped while the others are not. You need to merge the two trees into a new binary tree. The merge rule is that if two nodes overlap, then sum node values up as the new value of the merged node. Otherwise, the NOT null node will be used as the node of the new tree. Return the merged tree.

RECURSION: We can traverse both the given trees in a preorder fashion. At every step, we check if the current node exists (isn't null) for both the trees. If so, we add the values in the current nodes of both the trees and update the value in the current node of the first tree to reflect this sum obtained. At every step, we also call the original function mergeTrees() with the left children and then with the right children of the current nodes of the two trees. If at any step, one of these children happens to be null, we return the child of the other tree(representing the corresponding child subtree) to be added as a child subtree to the calling parent node in the first tree. At the end, the first tree will represent the required resultant merged binary tree.

K-th Symbol in Grammar: We build a table of n rows (1-indexed). We start by writing 0 in the 1st row. Now in every subsequent row, we look at the previous row and replace each occurrence of 0 with 01, and each occurrence of 1 with 10. For example, for n = 3, the 1st row is 0, the 2nd row is 01, and the 3rd row is 0110. Given two integer n and k, return the kth (1-indexed) symbol in the nth row of a table of n rows.

RECURSION: We see that, for any level N, the first half of the string is the same as the string in N-1, the next half is just complement of it. The total number of items in level N is 2^N. The half mark of the string is marked by [2^(N-1)]-th item. So, for any level N: if K is in the first half, it is same as the Kth element in level N-1; if K is in the second half, it is the complement of the number in [K-2^(N-1)]-th position in level N-1. Run the recursion until the base case N == 1.

Inorder Successor of BST: Given the root of a binary search tree and a node p in it, return the in-order successor of that node in the BST. If the given node has no in-order successor in the tree, return null. The successor of a node p is the node with the smallest key greater than p.val.

RECURSION: We start our traversal with the root node and continue the traversal until our current node reaches a null value i.e. there are no more nodes left to process. At each step we compare the value of node p with that of node. If p.val >= node.val that implies we can safely discard the left subtree since all the nodes there including the current node have values less than p. However, if p.val < node.val, that implies that the successor must lie in the left subtree and that the current node is a potential candidate for inorder successor. Thus, we update our local variable for keeping track of the successor, successor, to node. Return successor at the end.

Determine Whether Matrix Can Be Obtained By Rotation: Given two n x n binary matrices mat and target, return true if it is possible to make mat equal to target by rotating mat in 90-degree increments, or false otherwise.

ROTATION: Initialize four variables (one, two, three, four) equal to True. Iterate through every cell of mat. Test if the cell[i][j] is not equal to each rotation: target[i][j], target[j][m-i-1], target[n-i-1][m-j-1], target[n-j-1][i]; if so, set the corresponding variable to False. Outside of the loop, return True if any of the variables are still True.

Longest Consecutive Sequence: Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence. You must write an algorithm that runs in O(n) time.

SEQUENCE BUILDING: The numbers are stored in a set to allow O(1) lookups, and we only attempt to build sequences from numbers that are not already part of a longer sequence. This is accomplished by first ensuring that the number that would immediately precede the current number in a sequence is not present, as that number would necessarily be part of a longer sequence. 1. Create a set out of the list of nums 2. Iterate through nums. If num doesn't have a left neighbor, it is the start of a sequence so try and build the longest sequence (by checking if next num is in set). 3. Keep track of longest sequence

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.

SLIDING WINDOW, ASSOCIATIVE ARRAY: Using the sliding window technique, we set up pointers left = 0 and right = 0. We also keep track of a frequency array to know the most frequent character in a given window. We know that our current window / substring is valid when the number of characters that need to be replaced is <= k. We will need to know how many letters in our substring that we need to replace. To find out the lettersToReplace = (end - start + 1) - mostFreqLetter (pretty much you take the size of the window minus the most freq letter that is in the current window). Now that we know how many characters that need to be replaced in our window, we can deduce that if lettersToReplace > k then the window is invalid and we decrease the window size from the left.

Lemonade Change: At a lemonade stand, each lemonade costs $5. Customers are standing in a queue to buy from you and order one at a time (in the order specified by bills). Each customer will only buy one lemonade and pay with either a $5, $10, or $20 bill. You must provide the correct change to each customer so that the net transaction is that the customer pays $5. Given an integer array bills where bills[i] is the bill the ith customer pays, return true if you can provide every customer with the correct change, or false otherwise.

SIMULATION: Let's try to simulate giving change to each customer buying lemonade. Initially, we start with no five dollar bills, and no ten dollar bills. If a customer brings a $5 bill, then we take it. If a customer brings a $10 bill, we must return a five dollar bill. If we don't have a five dollar bill, the answer is False, since we can't make correct change. If a customer brings a $20 bill, we must return $15. If we have a $10 and a $5, then we always prefer giving change in that, because it is strictly worse for making change than three $5 bills. Otherwise, if we have three $5 bills, then we'll give that.Otherwise, we won't be able to give $15 in change, and the answer is False.

Longest Substring with 'K' Distinct Characters: Given a string s, find the length of the longest substring with k distinct characters.

SLIDING WINDOW, ASSOCIATIVE ARRAY: Use a window (start and end pointers), and add chars to the window until the number of unique chars exceeds k. Use an associative array (length 26) to keep track of which chars are in current window. If unique chars ever exceed k (helper function to check which counts all chars in associative array), remove the first element from window and decrement from associative array until the new window is valid. For every iteration, if window length is greater than max, set new max.

Find All Anagrams in a String: Given two strings s and p, return an array of all the start indices of p's anagrams in s. You may 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.

SLIDING WINDOW, ASSOCIATIVE ARRAY: Use an associative array to keep track of the count of letters in the anagram (chars = [0] * 26 or use Counter). Increment each spot in the array for each letter in the p string. Start and end initialized to 0; increment end until it reaches p.length, decrementing the appropriate character count in the associative array (if the value at the array was >= 0, this means it was part of the anagram so decrement diff as well). If after the loop diff == 0, s started with an anagram so add index 0 to the solutions. Loop until end reaches s.length: increment start, restoring back to associative array and incrementing diff if it was part of the anagram; increment end, removing from associative array and decrementing diff if it is part of the anagram; if diff == 0, this start index is a solution.

Permutation in String: Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise. In other words, return true if one of s1's permutations is the substring of s2.

SLIDING WINDOW: Instead of generating the HashMap afresh for every window considered in s2, we can create the HashMap just once for the first window in s2. Then, later on when we slide the window, we know that we remove one preceding character and add a new succeeding character to the new window considered. Thus, we can update the HashMap by just updating the indices associated with those two characters only. Again, for every updated HashMap, we compare all the elements of the HashMap for equality to get the required result. The last approach can be optimized, if instead of comparing all the elements of the HashMaps for every updated s2_map corresponding to every window of s2 considered, we keep a track of the number of elements which were already matching in the earlier HashMap and update just the count of matching elements when we shift the window towards the right. To do so, we maintain a count variable, which stores the number of characters (out of the 26 alphabets), which have the same frequency of occurrence in s1 and the current window in s2. When we slide the window, if the deduction of the last element and the addition of the new element leads to a new frequency match of any of the characters, we increment the count by 1. If not, we keep the count intact. But, if a character whose frequency was the same earlier (prior to addition and removal) is added, it now leads to a frequency mismatch which is taken into account by decrementing the same count variable. If, after the shifting of the window, the count evaluates to 26, it means all the characters match in frequency totally. So, we return a True in that case immediately.

Longest Substring without Repeating Characters: Given a string s, find the length of the longest substring without repeating characters.

SLIDING WINDOW: Use a HashSet to keep track of characters in current substring. Use left and right pointers to build sliding window. If the char at right index is not in seen, add the char to the set and increment right. Otherwise, remove the char at the left index from the set and increment left.

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.

SLIDING WINDOW: Use a dict_t = Counter(t) to keep track of the counts of each character in t. Use required = len(dict_t) to count the number of unique characters in t. Use formed = 0 to count the number of t characters we have accounted for (with correct frequencies) in our current window. Use window_counts = {} to keep track of the frequency of characters in current window. Use ans = (float("inf"), None, None) to keep track of (length of minimum window, right, left) for the answer. We start with two pointers, left and right initially pointing to the first element of the string S. We use the right pointer to expand the window until we get a desirable window i.e. a window that contains all of the characters of T. Once we have a window with all the characters, we can move the left pointer ahead one by one. If the window is still a desirable one we keep on updating the minimum window size. If the window is not desirable any more, we repeat step 2 onwards.

Special Array With X Elements Greater Than or Equal X: You are given an array nums of non-negative integers. nums is considered special if there exists a number x such that there are exactly x numbers in nums that are greater than or equal to x. Notice that x does not have to be an element in nums. Return x if the array is special, otherwise, return -1. It can be proven that if nums is special, the value for x is unique.

SORTING: Sort the array in reverse order: nums.sort(reverse=True) Find x: while i < len(nums) and nums[i] > i: i += 1 Check condition: return -1 if i < len(nums) and i == nums[i] else i

Longest Subsequence With Limited Sum: You are given an integer array nums of length n, and an integer array queries of length m. Return an array answer of length m where answer[i] is the maximum size of a subsequence that you can take from nums such that the sum of its elements is less than or equal to queries[i]. A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements.

SORTING / GREEDY / BINARY SEARCH: Sort nums and convert it into presum. We can re-use the nums array for this. Initialize an empty array answer. Iterate over queries, for each query query, we use binary search to find its insertion index index and add index to answer. Return answer when the iteration stops. Use index = bisect.bisect_right(nums, query) to find the insertion index.

Sort List: Given the head of a linked list, return the list after sorting it in ascending order.

SORTING, DIVIDE AND CONQUER The Top Down approach for merge sort recursively splits the original list into sublists of equal sizes, sorts each sublist independently, and eventually merge the sorted lists. Let's look at the algorithm to implement merge sort in Top Down Fashion. Recursively split the original list into two halves. The split continues until there is only one node in the linked list (Divide Phase). To split the list into two halves, we find the middle of the linked list using the Fast and Slow pointer approach as mentioned in Find Middle Of Linked List. Recursively sort each sublist and combine it into a single sorted list. (Merge Phase). This is similar to the problem Merge Two Sorted Linked Lists

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.

SORTING: Two strings are anagrams if and only if their character counts (respective number of occurrences of each character) are the same. We can transform each string s into a character count, count, consisting of 26 non-negative integers representing the number of a's, b's, c's, etc. We use these counts as the basis for our hash map. In python, the representation will be a tuple of the counts. For example, abbccc will be (1, 2, 3, 0, 0, ..., 0), where again there are 26 entries total.

Palindrome Number: Given an integer x, return true if x is palindrome integer. An integer is a palindrome when it reads the same backward as forward. For example, 121 is a palindrome while 123 is not.

STACK: Add every digit to stack on first pass through. Pop from stack on next pass through and make sure digits match. Use % and / operators.

Valid Parantheses: 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.

STACK: Add to stack whenever '(' is encountered. Pop from stack whenever ')' is encountered. Ensure that stack is empty at the bottom (O(n)).

Backspace String Compare: Given two strings s and t, return true if they are equal when both are typed into empty text editors. '#' means a backspace character. Note that after backspacing an empty text, the text will continue empty.

STACK: Build each string using a stack (push on characters or pop off if it is a backspace and not empty). Convert each stack to a string using ''.join(stack).

Decode String: Given an encoded string, return its decoded string. The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer. You may assume that the input string is always valid; there are no extra white spaces, square brackets are well-formed, etc. Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k.

STACK: Use a count_stack to keep track of how many times to repeat fragment and a res_stack to keep track of fragments. If the character is a digit, find the full number and push it to count_stack. If the character is '[', push the current fragment to the stack and set the res to be "". If the character is ']', create a new string by repeating the fragment popped from res_stack (using count_stack frequency). This becomes the new res. Otherwise, append the character to the current res.

Implement Queue using Stacks: Implement a first in first out (FIFO) queue using only two stacks. The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty). Implement the MyQueue class: - void push(int x) Pushes element x to the back of the queue. - int pop() Removes the element from the front of the queue and returns it. - int peek() Returns the element at the front of the queue. - boolean empty() Returns true if the queue is empty, false otherwise.

STACK: Use two stacks and a front variable. On push(), push the element to s1 and set front if s1 was empty. On pop(), if s2 is empty, pop all the elements off of s1 and push to s2; then, return s2.pop(). On peek(), return s2.peek() unless s2 is empty in which case you should return front. On empty(), return if both s1 and s2 are empty.

Min Stack: Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. Implement the MinStack class: -MinStack() initializes the stack object. -void push(int val) pushes the element val onto the stack. -void pop() removes the element on the top of the stack. -int top() gets the top element of the stack. -int getMin() retrieves the minimum element in the stack.

STACK: Use two stacks: one for actual elements and one to keep track of min value. If the current value is the new minimum, push it onto the min stack. If you are popping off the minimum of the stack, pop it off the min stack as well.

Reverse Words in a String: Given an input string s, reverse the order of the words. A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. Return a string of the words in reverse order concatenated by a single space. Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a single space separating the words. Do not include any extra spaces.

STANDARD LIBRARY: Use built in split and reverse methods.

Repeated Substring Pattern: Given a string s, check if it can be constructed by taking a substring of it and appending multiple copies of the substring together.

STRING MATCHING: The maximum length of a "repeated" substring that you could get from a string would be half it's length. For example, with s = "abcdabcd", "abcd" of len = 4 is the repeated substring. You cannot have a substring > (len(s)/2), that can be repeated. So, when ss = s + s , we will have at least 4 parts of "repeated substring" in ss. (s+s)[1:-1] => with this we are removing 1st char and last char. Out of 4 parts of repeated substring, 2 parts will be gone (they will no longer have the same substring). ss.find(s) != -1 => But still we have 2 parts out of which we can make s. And that's how ss should still have s, if s has repeated substring.

Merge Two Sorted Lists: You are given the heads of two sorted linked lists list1 and list2. Merge the two lists in a one sorted list. The list should be made by splicing together the nodes of the first two lists. Return the head of the merged linked list.

TWO POINTER: Create a new head node which is the smallest of the two head nodes in each list. Create a cur pointer to this new head, a cur1 pointer to the first list, and a cur2 pointer to the second list. While either list is not None, loop. If one list is None, set the cur next to be the rest of the other list and break the loop. Otherwise, set cur next to be the smallest of the next two nodes (create a new ListNode with same val) and increment appropriately.

Reverse Words in a String III: Given a string s, reverse the order of characters in each word within a sentence while still preserving whitespace and initial word order.

TWO POINTER: Find the start and end index of every word given by start_index and end_index. Swap the characters in the word pointed by start_index and end_index. Increment start_index by 1 and decrement end_index by 1. Repeat steps 2 and 3 until start_index < end_index.

Intersection of Two Linked Lists: Given the heads of two singly linked-lists headA and headB, return the node at which the two lists intersect. If the two linked lists have no intersection at all, return null.

TWO POINTER: Let A be the portion of the first linked list before the intersection. Let B be the portion of the second linked list before the intersection. Let C be the portion of both linked lists that is the intersection. Now, if we force each pointer to move a + c + b = b + c + a steps, then they will meet at the point where the two lists intersect. This can be done by have each pointer iterate through a separate list entirely, and then set the pointer to the head of the other list when it reaches the end and stop when the two pointers intersect.

Odd Even Linked List: Given the head of a singly linked list, group all the nodes with odd indices together followed by the nodes with even indices, and return the reordered list. The first node is considered odd, and the second node is even, and so on. Note that the relative order inside both the even and odd groups should remain as it was in the input.

TWO POINTER: Let head be the head of the odd list. Let even_head be the head of the even list. Let odd / even be the variables to traverse each list. While even != None and even.next != None, form the odd list and even list with the next nodes. Outside of the loop, attach the end of the odd list to the original even_head.

Rotate List: Given the head of a linked list, rotate the list to the right by k places.

TWO POINTER: Naive two-pass: use a cur variable to traverse the list and find the list size. Attach the end of the list to the head. Then, iterate again up to the node of index (size - k % size) — this will be the new head. Set the previous node's next to None and return the new head. Optimize this solution to be one pass by using a fast and slow pointer. The slow pointer should lag behind by (size - k % size) spots.

Is Subsequence: Given two strings s and t, return true if s is a subsequence of t, or false otherwise. A subsequence of a string is a new string that is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (i.e., "ace" is a subsequence of "abcde" while "aec" is not).

TWO POINTER: Use an index for string s and an index for string t. Iterate through s and for each char, keep incrementing t index until the characters match. If index t exceeds bounds, return false. If index s reaches the end, return true.

Longest Common Prefix: Write a function to find the longest common prefix string amongst an array of strings. If there is no common prefix, return an empty string "".

VERTICAL SCANNING: Imagine a very short string is the common prefix at the end of the array. The above approach will still do S comparisons. One way to optimize this case is to do vertical scanning. We compare characters from top to bottom on the same column (same character index of the strings) before moving on to the next column.

Insert Intervals: You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval. Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if necessary). Return intervals after the insertion.

MERGE INTERVALS: Insert the newInterval into the given list of intervals using linear search. 1. Iterate over the list and find the first interval with a start value greater than the newInterval. 2. Insert newInterval just before this interval or at the end of the list if no such interval exists. 3. Run merge intervals algorithm on this interval list.

Two Sum Less Than Or Equal To K: Find all pairs of values in an array that sum to less than or equal to the value k.

TWO POINTER: Sort the array and use Two Pointer two sum approach.

Two Sum II - Input Array is Sorted: Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number.

TWO POINTER: Use two pointer method (low and high pointer converging).

Pascal's Triangle: Given an integer numRows, return the first numRows of Pascal's triangle. In Pascal's triangle, each number is the sum of the two numbers directly above it.

DYNAMIC PROGRAMMING: Create a new list of lists called triangle. For each row, create a new list called row. Iterate from 0 to the rowIndex (inclusive). If the index is 0 or rowIndex (first or last), simply add 1 to the row. Otherwise, add the sum of the previous row's previous column + the previous row's current column. After the loop through the columns, add this row to the triangle list.

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.

BACKTRACK: Use a backtrack helper function: backtrack(list, subset, nums, start). Add subset to list, then for i from start to end of nums, add nums[i] to subset, recurse with start + 1, and "backtrack" by removing the last element from subset. The last step is the "backtracking" technique. DFS solution: at each index, you have the choice to add that index or skip it. Add it to current subset, recurse with i+1 index, then pop it from subset, and recurse with i+1 index. If i has reached the end of the length, add that subset to the result.

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.

MATH: 1. Find the expected sum by adding all indices up to the number. 2. Find the actual sum by adding all the elements in the array. 3. The missing number is the difference between the expected and actual.

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.

RECURSION: The simplest strategy here is to use recursion. Check if p and q nodes are not None and their values are equal. If all checks pass, do the same for the child nodes recursively.

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.

BACKTRACK: Use a backtrack helper function: backtrack(list, subset, nums, start). Add subset to list, then for i from start to end of nums, add nums[i] to subset, recurse with start + 1 (if not a duplicate), and remove the last element from subset. The last step is the "backtracking" technique. DFS solution: at each index, you have the choice to add that index or skip it. Add it to current subset, recurse with i+1 index, then pop it from subset, and recurse with i+1 index. If i has reached the end of the length, add that subset to the result. Same solution as Subsets (above), but skip any duplicate elements (continue in the for-loop instead of recursing if i > start and nums[i] == nums[i - 1]).

Best Time to Buy and Sell a Stock: You are given an array prices where prices[i] is the price of a given stock on the ith day. You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock. Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.

DYNAMIC PROGRAMMING: 1. Keep track of the min_price and the max_profit. 2. Whenever you encounter a new minimum, set the min_price. 3. Whenever you encounter a new max_profit, set the max_profit. For every element, we are calculating the difference between that element and the minimum of all the values before that element and we are updating the maximum profit if the difference thus found is greater than the current maximum profit.

Majority Element II: Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times.

BOYER-MOORE VOTING ALGORITHM: There can be at most two majority elements which are more than ⌊n/3⌋ times. Now figuring out the majority elements which show up more than ⌊n/3⌋ times is not that hard anymore. Using the intuition presented in the beginning, we only need four variables (we needed two for Majority Element original question): two for holding two potential candidates and two for holding two corresponding counters. Similar to the above case, both candidates are initialized as None in the beginning with their corresponding counters being 0. While going through the array: - If the current element is equal to one of the potential candidate, the count for that candidate is increased while leaving the count of the other candidate as it is. - If one of the counters reaches zero, the candidate associated with that counter will be replaced with the next element if the next element is not equal to the other candidate as well. - Both counters are decremented only when the current element is different from both candidates.

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.

DYNAMIC PROGRAMMING: Divide the problem into a smaller problem: for each index i, find the max subarray that must end at i. Keep track of the max and the max_ending_here. Iterate through array and set max_ending_here = max(max_ending_here + nums[i], nums[i]). Set max = max(max_so_far, max_ending_here). Return max outside of the loop.

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.

DIVIDE AND CONQUER: Merge pairs of sorted lists.

Fibonacci Sequence: Find the nth term of the Fibonacci sequence.

DYNAMIC PROGRAMMING (more efficient than recursive solution): num[0] = 0; num[1] = 1; From i = 2 to n, num[i] = num[i - 1] + num[i -2]; Return num[n].

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?

DYNAMIC PROGRAMMING: The total number of ways to reach ith is equal to sum of ways of reaching (i-1)th step and ways of reaching (i−2)th step. Use an array and DP. arr[1] = 1; arr[2] = 2; iterate through and arr[i] = arr[i - 1] + arr[i + 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 recently used key. The functions get and put must each run in O(1) average time complexity.

HASHING: Use a HashMap and a custom implementation of a DLL. The DLL should act like a queue where we add the most recently viewed to the tail and pop the oldest one from the head. It should have functions to addNode (to head) and removeNode (by passing in the node, we can use memory references and do this in constant time instead of traversing). For getting, lookup if it exists using the HashMap and move it to the head (remove and then add to head) since it was the most recently accessed. For put, if the node isn't already in the HashMap, add it to the HashMap and to the head of the DLL. If this causes the capacity to exceed its limit, pop off the tail. If it is already in the HashMap, update the value and move it to the head of the DLL (remove and then add to head). ---------------------------------------------------------------------- ORDERED DICTIONARY: Use get/in/move_to_end(key)/popitem(last=False) methods of OrderedDict class from collections.

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.

HASHING: Use a HashSet and add each node while cur != None. If it is within the set already, there is a cycle. ---------------------------------------------------------------------- TWO POINTERS: If you need O(1) space, use fast and slow pointers (both start at head). While fast.next != None and fast.next.next != None, if they are ever equal, there is a cycle. Otherwise, return false.

Increasing Order Search Tree: Given the root of a binary search tree, rearrange the tree in in-order so that the leftmost node in the tree is now the root of the tree, and every node has no left child and only one right child.

INORDER TRAVERSAL: An in-order traversal of the nodes will yield all the values in increasing order. Once we have traversed all the nodes in increasing order, we can construct new nodes using those values to form the answer. During the traversal, we'll construct the answer on the fly, reusing the nodes of the given tree by cutting their left child and adjoining them to the answer.

Find Mode in Binary Search Tree: Given the root of a binary search tree (BST) with duplicates, return all the mode(s) (i.e., the most frequently occurred element) in it. If the tree has more than one mode, return them in any order. Assume a BST is defined as follows: The left subtree of a node contains only nodes with keys less than or equal to the node's key. The right subtree of a node contains only nodes with keys greater than or equal to the node's key. Both the left and right subtrees must also be binary search trees.

INORDER TRAVERSAL: Do an inorder traversal of the tree (since the values will be in order) and keep track of a current count for each val. As long as the current val is equal to the previous val, increment the current count; otherwise, set it to 1. If the current count reaches the maximum, append it to the result array. If the current count exceeds the maximum, reset the result array to just include this current value and update max count.

Check if It Is a Straight Line: You are given an array coordinates, coordinates[i] = [x, y], where [x, y] represents the coordinate of a point. Check if these points make a straight line in the XY plane.

MATH: Compare the slopes for the line segments defined by each consecutive pair of points. The slope of the line is the slope of the first two points (dy = y1 - y0, dx = x1 - x0). yi - yi-1 / xi - xi-1 == dy / dx. Note: To avoid divide by zero error, use dx(yi - yi-1) == dy(xi - xi-1).

Max Increase to Keep City Skyline: There is a city composed of n x n blocks, where each block contains a single building shaped like a vertical square prism. You are given a 0-indexed n x n integer matrix grid where grid[r][c] represents the height of the building located in the block at row r and column c. A city's skyline is the the outer contour formed by all the building when viewing the side of the city from a distance. The skyline from each cardinal direction north, east, south, and west may be different. Return the maximum total sum that the height of the buildings can be increased by without changing the city's skyline from any cardinal direction.

MATH: For grid[i][j], it can't be higher than the maximum of its row nor the maximum of its col. So the maximum increasing height for a building at (i, j) is min(row[i], col[j]) - grid[i][j].

Diagonal Traverse: Given an m x n matrix mat, return an array of all the elements of the array in a diagonal order.

MATH: The key here is to realize that the sum of indices on all diagonals are equal. Use a dictionary which maps the index sum to the list of elements on the diagonal. At the end, reverse every other list when adding the final result array.

Sort the Matrix Diagonally: A matrix diagonal is a diagonal line of cells starting from some cell in either the topmost row or leftmost column and going in the bottom-right direction until reaching the matrix's end. Given an m x n matrix mat of integers, sort each matrix diagonal in ascending order and return the resulting matrix.

MATH: mat[i][j] on the same diagonal have same value of i - j. For each diagonal, put its elements together, sort, and set them back in the original array.

Binary Tree Paths: Given the root of a binary tree, return all root-to-leaf paths in any order. A leaf is a node with no children.

RECURSION: Keep track of a paths top level list. Use recursive helper functions and recurse to left and right subtrees. In the helper function, if the node is None, return. If the node has no children, add the node to the path and append this path to paths, and return. Else, add the node to the path and recurse.

Evaluate Boolean Binary Tree: You are given the root of a full binary tree with the following properties: Leaf nodes have either the value 0 or 1, where 0 represents False and 1 represents True. Non-leaf nodes have either the value 2 or 3, where 2 represents the boolean OR and 3 represents the boolean AND. Return the boolean result of evaluating the root node.

RECURSION: Use a switch statement (switch (root.val)). Case 0: return false. Case 1: return true. Case 2: return evaluateTree(root.left) || evaluateTree(root.right). Case 4: return evaluateTree(root.left) && evaluateTree(root.right).

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

ROTATION: Find the new starting index (after rotation) using len(nums) - (k % len(nums)). The % is needed if the rotation amount is greater than the array size. Then, copy over the elements starting from this index to a new array. Finally, copy over the elements from the new array to the old array. ---------------------------------------------------------------------- REVERSAL (Optimized): This approach is based on the fact that when we rotate the array k times, k elements from the back end of the array come to the front and the rest of the elements from the front shift backwards. In this approach, we firstly reverse all the elements of the array. Then, reversing the first k elements followed by reversing the rest n−k elements gives us the required result.

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. Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as eval().

STACK: We know that there could be 4 types of operations - addition (+), subtraction (-), multiplication (*) and division (/). Without parenthesis, we know that, multiplication and division operations would always have higher precedence than addition and subtraction. Scan the input string s from left to right and evaluate the expressions based on the following rules: 1. If the current character is a digit 0-9 (operand), add it to the number current_number. 2. Otherwise, the current character must be an operation (+,-,*, /). Evaluate the expression based on the type of operation. - Addition (+) or Subtraction (-): We must evaluate the expression later based on the next operation. So, we must store the current_number to be used later. Let's push the current_number in the Stack. Stack data structure follows Last In First Out (LIFO) principle. Hence, the last pushed number in the stack would be popped out first for evaluation. In addition, when we pop from the stack and evaluate this expression in the future, we need a way to determine if the operation was addition or subtraction. To simplify our evaluation, we can push -current_number in a stack if the current operation is subtraction and assume that the operation for all the values in the stack is addition. - Multiplication (*) or Division (/): Pop the top values from the stack and evaluate the current expression. Push the evaluated value back to the stack. 3. Once the string is scanned, pop from the stack and add to the result (sum the entire stack).

Squares of a Sorted Array: Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order.

TWO POINTER: 1. Use two points at front and end of array. 2. Iterate backwards: choose the larger absolute value from the pointers and add that to the new sorted array, incrementing/decrementing that pointer.

Duplicate Zeroes: Given a fixed-length integer array arr, duplicate each occurrence of zero, shifting the remaining elements to the right. Note that elements beyond the length of the original array are not written. Do the above modifications to the input array in place and do not return anything.

TWO POINTER: We will do two passes on the array and use a two-pointer approach for the second pass. 1. On the first pass, count the number of zeroes in the array that will be duplicated (stop for loop at length - possible_dups to account for zeroes that won't be duplicated at the end because they are cut off). 2. On the second pass, iterate backwards from the length - possible_dups element of the array. Copy over non-zero elements once and copy zero elements twice to another pointer that starts at the back of the array.

Middle of the Linked List: Given the head of a singly linked list, return the middle node of the linked list. If there are two middle nodes, return the second middle node.

TWO POINTERS: When traversing the list with a pointer slow, make another pointer fast that traverses twice as fast. When fast reaches the end of the list (end loop if fast == None or fast.next == None), slow must be in the middle.

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.

BFS: 1. Use a map called graph to map a parent course to its children courses (the courses it is a prerequisite for). Use another map to keep track of the in-degree of all the children. For child, parent in prerequisites: graph[parent].append(child) and in_degree[child] += 1. 2. Now use a queue and initialize with all of the courses that have an in-degree of 0 (these are the sources). While this queue is not empty, pop left, increment visited count, and decrement the in-degree for every child in graph[vertex] (if in-degree goes to 0, append this course to the queue). 3. At the end, return visited == numCourses.

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). Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums. You must write an algorithm with O(log n) runtime complexity.

BINARY SEARCH: Run a modified binary search. If nums[left] <= nums[mid], then the array was left rotated (pivot was on left side of original array). In this case, if target is between nums[left] and nums[mid], search in the subarray from left to mid-1. Otherwise, search in the subarray from mid+1 to high. If the array was right rotated (pivot was on the right side of original array), then if target is between nums[mid] and nums[high] then search in the subarray from mid+1 to high. Otherwise, search in the subarray from low to mid-1.

Bus Routes: You are given an array routes representing bus routes where routes[i] is a bus route that the ith bus repeats forever. For example, if routes[0] = [1, 5, 7], this means that the 0th bus travels in the sequence 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... forever. You will start at the bus stop source (You are not on any bus initially), and you want to go to the bus stop target. You can travel between bus stops by buses only. Return the least number of buses you must take to travel from source to target. Return -1 if it is not possible.

BFS: Instead of thinking of the stops as nodes (of a graph), think of the buses as nodes. We want to take the least number of buses, which is a shortest path problem, conducive to using a breadth-first search. We perform a breadth first search on bus numbers. When we start at S, originally we might be able to board many buses, and if we end at T we may have many targets for our goal state. One difficulty is to efficiently decide whether two buses are connected by an edge. They are connected if they share at least one bus stop. Whether two lists share a common value can be done by set intersection (HashSet), or by sorting each list and using a two pointer approach. If any bus routes intersect (there is a route in both), then there should be an edge between both of these routes. Keep track of a seen set and targets set (initialize with routes that contain source or target, respectively). If you pop a route that intersects with target, return the depth; otherwise, append any intersecting routes that haven't been seen yet with an incremented depth value. To make our search easy, we will annotate the depth of each node: info[0] = node, info[1] = depth.

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.

BFS: The main algorithm is built around a loop iterating through the queue. At each iteration, we pop out an element from the head of the queue. Then we do some particular process with the popped element. More importantly, we then append neighbors of the popped element into the queue, to keep the BFS process running. Usually in BFS algorithms, we keep a visited table which records the visited candidates. The visited table helps us to avoid repetitive visits. But as one notices, rather than using the visited table, we reuse the input grid to keep track of our visits, i.e. we were altering the status of the input grid in-place. This in-place technique reduces the memory consumption of our algorithm. Also, it has a constant time complexity to check the current status (i.e. array access, grid[row][col]), rather than referring to the visited table which might be of constant time complexity as well (e.g. hash table) but in reality could be slower than array access. We use a delimiter (i.e. (row=-1, col=-1)) in the queue to separate cells on different levels. In this way, we only need one queue for the iteration. As an alternative, one can create a queue for each level and alternate between the queues, though technically the initialization and the assignment of each queue could consume some extra time.

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 finish all courses, return an empty array.

BFS: This is the same approach as Course Schedule, but maintain a list of the sequence to return if visited == numCourses. Initialize a queue to keep a track of all the nodes in the graph with 0 in-degree. Iterate over all the edges in the input and create an adjacency list and also a map of each node's in-degree. Add all the nodes with 0 in-degree to the queue. The following steps are to be done until the queue becomes empty. Pop a node from the queue. For all the neighbors of this node, reduce their in-degree by 1. If any of the nodes' in-degree reaches 0, add it to the queue. Add the original node to the list, maintaining topologically sorted order.

Binary Search: Given an array of integers nums which is sorted in ascending order, and an integer target, write a function to search target in nums. If target exists, then return its index. Otherwise, return -1.

BINARY SEARCH: 1. Keep track of start and end indices. 2. While start <= end, find mid. 3. If the value at mid is equal to the target, return mid. 4. Otherwise, check if mid is less than / greater than target and change start / end indices appropriately. Note: Finding mid with start + (end - start) / 2 is better than (start + end) / 2 due to potential integer overflow in languages such as Java.

Find Pivot Index: Given an array of integers nums, calculate the pivot index of this array. The pivot index is the index where the sum of all the numbers strictly to the left of the index is equal to the sum of all the numbers strictly to the index's right. If the index is on the left edge of the array, then the left sum is 0 because there are no elements to the left. This also applies to the right edge of the array. Return the leftmost pivot index. If no such index exists, return -1.

BINARY SEARCH: 1. Keep track of the left_sum and right_sum. left_sum starts at 0 and right_sum starts as the sum of the array. 2. Iterate over the array, decrementing right_sum with the value at the current index. 3. If left_sum == right_sum, return the current index, otherwise increment left_sum with the value at the current index. Note: Make sure you are not including the value at the current index in the left and right sums.

First Bad Version: Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad. You are given an API bool isBadVersion(version) which returns whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.

BINARY SEARCH: 1. Run binary search from integers 1 to n, keeping track of the first bad version in a local variable. 2. When start > end, the loop should end and return the value of first bad version.

Find First and Last Position of Element in Sorted Array: Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. If target is not found in the array, return [-1, -1]. You must write an algorithm with O(log n) runtime complexity.

BINARY SEARCH: Find greatest element less than target. Find smallest element larger than target. Return the start and end of target.

Find Minimum in Rotated Sorted Array: Suppose an array of length n sorted in ascending order is rotated between 1 and n times. Given the sorted rotated array nums of unique elements, return the minimum element of this array.

BINARY SEARCH: Run binary search to find the inflection point (where the rotation occurs). Compare the middle element to the first element to determine if you should recurse to the left subarray or right subarray. 1. Find the mid element of the array. 2. If mid element > first element of array this means that we need to look for the inflection point on the right of mid. 3. If mid element < first element of array this that we need to look for the inflection point on the left of mid. 4. We stop our search when we find the inflection point, when either of the two conditions is satisfied: - nums[mid] > nums[mid + 1]: Hence, mid+1 is the smallest. - nums[mid - 1] > nums[mid]: Hence, mid is the smallest.

Search a 2D Matrix: Write an efficient algorithm that searches for a value target in an m x n integer matrix 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.

BINARY SEARCH: Treat the matrix as a sorted list and run binary serach. Use start (0) and end (num_rows * num_cols - 1) pointers. New mid = (start + end) / 2. Check matrix[mid / num_cols][mid % num_cols] and compare to target to determine if start or end should change to be right next to mid.

Single Element in a Sorted Array: You are given a sorted array consisting of only integers where every element appears exactly twice, except for one element which appears exactly once. Return the single element that appears only once.

BINARY SEARCH: We can observe that for each pair, the first element takes an even position and second element takes an odd position. This pattern will be missed when single element is appeared in the array. 1. Initialize left and right pointers: left points to start of list, right points to end of the list. 2. Find mid. If mid is even, then it's duplicate should be in the next index. If mid is odd, then it's duplicate should be in previous index. Check these conditions: if they are satisfied, then the pattern is not missed up to this point, so check in the next half of the array (i.e. left = mid + 1). If the condition is not satisfied, then the pattern was missed somewhere up to this point, so the single number must be before mid. Update end to mid. 3. When left and right pointers meet, return nums[left] (the number the pointers converged on).

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

BIT MANIPULATION: Keep a count variable. While the integer is not 0, check if the next bit is 1 by &'ing the integer with 1 and incrementing count if necessary. Then, bitshift the integer one bit (n = n >> 1).

Reverse Bits: Reverse bits of a given 32 bit unsigned integer.

BIT MANIPULATION: Start int result at 0. For i from 0 to 32 (exclusive), result <<= 1, result += n&1, num >>= 1. Return result.

Power of Four: Given an integer n, return true if it is a power of four. Otherwise, return false. An integer n is a power of four, if there exists an integer x such that n == 4^x.

BIT MANIPULATION: Take the square root of the number (if invalid, return false) and cast to int. To verify that there is an integer square root, square this number to compare it against the original. Then, simply check power of two logic (n & n - 1 == 0). ---------------------------------------------------------------------- MATH: If num is a power of four, then log_2(num) is an integer. Hence let's simply check if log_2(num) is an even number.

Hamming Distance: The Hamming distance between two integers is the number of positions at which the corresponding bits are different. Given two integers x and y, return the Hamming distance between them.

BIT MANIPULATION: To get a binary number where each bit is 1 if the two bits are different, use XOR (will be 1 if two bits are opposite). xor = x ^ y. Then, iterate from 0 to 32 (because an integer is 32 bits) and count how many 1 bits there are. count += xor & 1, right shift xor.

Convert a Number to Hexadecimal: Given an integer num, return a string representing its hexadecimal representation. For negative integers, two's complement method is used. All the letters in the answer string should be lowercase characters, and there should not be any leading zeros in the answer except for the zero itself. Note: You are not allowed to use any built-in library method to directly solve this problem.

BIT MANIPULATION: Use a string as a map for hexadecimal digits: '0123456789abcdef'. Get the first four digits by &'ing the number with 15 (1111b). Get corresponding hex character from the map: map[hex_digit]. Prepend this hex character to the answer. Shift the number >> 4. Repeat this for up to 8 iterations. At the end, strip the left side of the answer of all zeroes.

Power of Two: Given an integer n, return true if it is a power of two. Otherwise, return false.

BIT MANIPULATION: pow(n, 2) = 1 << n = 0b10..0. pow(n, 2) - 1 = 0b011..1. Therefore, pow(2, n) & (pow(2, n) - 1) == 0 and if n is not a power of 2, then n & n - 1 != 0. Return n and not (n & n - 1).

Majority Element: Given an array nums of size n, return the majority element. The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array.

BOYER-MOORE: Essentially, what Boyer-Moore does is look for a suffix suf of nums where suf[0] is the majority element in that suffix. To do this, we maintain a count, which is incremented whenever we see an instance of our current candidate for majority element and decremented whenever we see anything else. Whenever count equals 0, we effectively forget about everything in nums up to the current index and consider the current number as the candidate for majority element.

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.

BRUTE FORCE: The naive approach to solve this problem is to use recursion and backtracking. For finding the solution, we check every possible prefix of that string in the dictionary of words, if it is found in the dictionary, then the recursive function is called for the remaining portion of that string. And, if in some function call it is found that the complete string is in dictionary, then it will return true. ---------------------------------------------------------------------- RECURSION WITH MEMOIZATION: In the previous approach we can see that many subproblems were redundant, i.e we were calling the recursive function multiple times for a particular string. To avoid this we can use memoization method, where an array memo is used to store the result of the subproblems. Now, when the function is called again for a particular string, value will be fetched and returned using the memo array, if its value has been already evaluated. Use @lru_cache above function definition to cache the result of recursive function call. Use an immutable set with frozenset(). ---------------------------------------------------------------------- DYNAMIC PROGRAMMING: The intuition behind this approach is that the given problem (s) can be divided into subproblems s1 and s2. Now, we'll move onto the process of DP array formation. We make use of DP array of size n+1, where n is the length of the given string. We also use two index pointers i and j, where i refers to the length of the substring (s′) considered currently starting from the beginning, and j refers to the index partitioning the current substring (s′) into smaller substrings s′(0,j) and s′(j+1,i). To fill in the DP array, we initialize the element DP[0] as true, since the null string is always present in the dictionary, and the rest of the elements of DP as false. We consider substrings of all possible lengths starting from the beginning by making use of index i. For every such substring, we partition the string into two further substrings s1′ and s2′ in all possible ways using the index j (Note that the i now refers to the ending index of s2). Now, to fill in the entry DP[i], we check if the DP[j] contains true, i.e. if the substring s1′ fulfills the required criteria, then we further check if s2' is present in the dictionary. If both the strings fulfill the criteria, we make DP[i] as true, otherwise as false.

Pascal's Triangle II: Given an integer rowIndex, return the rowIndexth (0-indexed) row of the Pascal's triangle. In Pascal's triangle, each number is the sum of the two numbers directly above it as shown:

BRUTE-FORCE RECURSION: In Pascal's triangle, each number is the sum of the two numbers directly above it. Let's say we had a function getNum(row_index, col_index), which gave us the col_indexth number in the row_indexth row, we could simply build the kth row by repeatedly calling getNum(...) for columns 0 to k. getNum(row_index, col_index) = getNum(row_index-1, col_index-1) + getNum(row_index-1, col_index). The recursion ends in some known base cases: The first row is just a single 111, i.e. getNum(0, ...) = 1 The first and last number of each row is 111, i.e. getNum(k, 0) = getNum(k, k) = 1 ---------------------------------------------------------------------- DYNAMIC PROGRAMMING: Simple memoization caches results of deep recursive calls and provides significant savings on runtime. But, it is worth noting that generating a number for a particular row requires only two numbers from the previous row. Consequently, generating a row only requires numbers from the previous row. Thus, we could reduce our memory footprint by only keeping the latest row generated, and use that to generate a new row. ---------------------------------------------------------------------- DYNAMIC PROGRAMMING (memory efficient): Notice that in the previous approach, we have maintained the previous row in memory on the premise that we need terms from it to build the current row. This is true, but not wholly. What we actually need, to generate a term in the current row, is just the two terms above it (present in the previous row). pascal[i][j] = pascal[i-1][j-1] + pascal[i-1][j]. pascal[j] was somehow generated in a previous instance. Currently, it holds the previous row value. pascal[j] (which holds the jth number of the previous row) must be read when writing out pascal[j] (the jth number of the current row). Obviously they are the same memory location, so a conflict exists: the previous row value of pascal[j] will be lost after the write-out. Is that ok? If we don't need to read the previous row value of pascal[j] anymore, is there any harm in writing out the current row value in its place? pascal[j] (which holds the jth number of the previous row) must be read when writing out pascal[j+1] (the j+1th number of the current row). These are two different memory locations, so there is no conflict.

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

DFS WITH MEMOIZATION: From previous analysis, we know that there are many duplicate calculations in the naive DFS approach. We should cache the results for the recursion so that any subproblem will be calculated only once (memoization). In computing, memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again. In our problem, we recursively call dfs(x, y) many times. But if we already know all the results for the four adjacent cells, we only need constant time. During our search if the result for a cell is not calculated, we calculate and cache it; otherwise, we get it from the cache directly.

Flood Fill: An image is represented by an m x n integer grid image where image[i][j] represents the pixel value of the image. You are also given three integers sr, sc, and color. You should perform a flood fill on the image starting from the pixel image[sr][sc]. To perform a flood fill, consider the starting pixel, plus any pixels connected 4-directionally to the starting pixel of the same color as the starting pixel, plus any pixels connected 4-directionally to those pixels (also with the same color), and so on. Replace the color of all of the aforementioned pixels with color. Return the modified image after performing the flood fill.

DFS: 1. If the starting color and new color are the same, return the image. 2. Otherwise, run DFS on the starting pixel. Check if row_index and col_index are within bounds and the pixel is the same as the starting color. 3. If so, change the pixel to the new color and run DFS on four neighboring pixels. 4. Otherwise, return.

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. The island is partitioned into a grid of square cells. 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 north, south, east, and west if the neighboring cell's height is less than or equal to the current cell's height. Water can flow from any cell adjacent to an ocean into the ocean. 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.

DFS: The naive approach would be to check every cell - that is, iterate through every cell, and at each one, start a traversal that follows the problem's conditions. That is, find every cell that manages to reach both oceans. This approach, however, is extremely slow, as it repeats a ton of computation. Instead of looking for every path from cell to ocean, let's start at the oceans and try to work our way to the cells. This will be much faster because when we start a traversal at a cell, whatever result we end up with can be applied to only that cell. However, when we start from the ocean and work backwards, we already know that every cell we visit must be connected to the ocean. If the input is empty, immediately return an empty array. 1. Initialize variables that we will use to solve the problem: Number of rows and columns in our matrix; two sets, one for the Atlantic Ocean and one for the Pacific Ocean that will be used for DFS (the data structure used to keep track of cells already visited has a double purpose - it also contains every cell that can flow into that ocean); a small array [(0, 1), (1, 0), (-1, 0), (0, -1)] that will help with DFS. 2. Figure out all the cells that are adjacent to each ocean, and run dfs on these cells (with respective atlantic or pacific sets) 3. Find the intersection, that is all cells that can flow into both oceans.

Most Stones Removed with Same Row or Column: On a 2D plane, we place n stones at some integer coordinate points. Each coordinate point may have at most one stone. A stone can be removed if it shares either the same row or the same column as another stone that has not been removed. Given an array stones of length n where stones[i] = [xi, yi] represents the location of the ith stone, return the largest possible number of stones that can be removed.

DFS: To build the graph, we will iterate over every pair of stones and add the edge between them if they have the same row or column in the adjacency list. We can then iterate over the stones and start DFS from it if it's not been traversed yet. Each time we start the DFS, we will increment the count of components. Create an adjacency list such that adj[v] contains all the adjacent vertices of vertex v. Initialize a hash set or array, visited, to track the visited vertices. Define a component_count variable and initialize it to zero. Iterate over each stone from 0 to length(stones), and if the stone is not in visited, start a DFS from it. Add every stone visited during the DFS to the visited. Every time a new DFS starts, increment the counter variable component_count by one. Return length(stones) - component_count.

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.

DFS: 1. Iterate through the entire grid 2. If current cell is a 1, increment counter and flip this cell to a 0 3. Run DFS on left, right, up, down cells and turn all adjacent 1s into 0s; return when out of bounds or a 0 cell is reached ---------------------------------------------------------------------- BFS: 1. Same method as DFS but add adjacent cells to a queue instead of recursing immediately; pop off and check adjacent cells while queue is not empty Time complexity is O(m*n) since both visit the whole grid twice.

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.

DYNAMIC PROGRAMMING: DP table is a one-dimensional array with the same length as nums. profit[0] = nums[0] profit[1] = max(nums[0], nums[1]) Then, the recurrence is profit[i] = max(profit[i-1], profit[i-2] + nums[i]). Return profit[len(nums)-1].

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.

DYNAMIC PROGRAMMING: For the iterative solution, we think in bottom-up manner. Before calculating F(i), we have to compute all minimum counts for amounts up to i. On each iteration i of the algorithm F(i) is computed as min⁡(F(i−cj)+1) for j from 0...n-1. Concretely, for each coin, iterate from the denomination value up to amount and update the DP table: dp[x] = min(dp[x], dp[x-coin] + 1). Base case: dp[0] = 0. All other indices are initialized to float('inf').

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.

DYNAMIC PROGRAMMING: Keep track of the min cost to get to the top from any given stair. Since you can start at index 0 or 1, the min cost for those is equal to their cost. From index 2 to the end, the min cost is equal to the min of the previous two min costs + the current cost. At the end of the loop, return the min of the min costs of the last two stairs.

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.

DYNAMIC PROGRAMMING: The problem is similar to the classic Knapsack problem. The basic idea is to understand that to partition an array into two subsets of equal sum, say subset_sum, the total_sum of given array must be twice the subset_sum. Now, the problem is to find the subset with a sum equal to a given target (half of total). We maintain a 2D array, dp[n][subset_sum] for an array element i and sum j in array nums. dp[i][j]=True if the sum j can be formed by array elements in subset nums[0]...nums[i], otherwise dp[i][j]=False. dp[i][j] is true if it satisfies one of the following conditions: Case 1) sum j can be formed without including ith element: if dp[i−1][j]==True. Case 2) sum j can be formed including ith element: if dp[i−1][j−nums[i]]==True. If there is a subset that can sum to this subset_sum (half of the total_sum), then we know there must be another equal subset in the array.

Count Primes: Given an integer n, return the number of prime numbers that are strictly less than n.

DYNAMIC PROGRAMMING: Use an associative array not_prime of booleans of size n. Iterate from 2 up to n. If not_prime at the current index is false, the current number is a prime so increment count. For either case, being a loop that starts at 2 and stops when i · j < n. Update all indices i · j in not_prime with true, indicating these numbers are not primes because they are multiples of the current number being considered.

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.

DYNAMIC PROGRAMMING: When (n == 0 or m == 0) the function always returns 1 since the robot can't go left or up. For all other cells, the result = grid[i - 1, j] + grid[i, j - 1]. Use an associative array to build out the whole matrix and return grid[m-1][n-1].

Range Sum Query - Immutable: Given an integer array nums, handle multiple queries of the following type: - Calculate the sum of the elements of nums between indices left and right inclusive where left <= right. Implement the NumArray class: - NumArray(int[] nums) Initializes the object with the integer array nums. - int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive (i.e. nums[left] + nums[left + 1] + ... + nums[right]).

DYNAMIC PROGRAMMING: When initializing nums, set each element to the cumulative sum of all elements prior to it and itself (nums[i] += nums[i - 1]). Then, when querying, just return the difference nums[right] - nums[left - 1] since both bounds are inclusive. Edge case: if left is 0, just return nums[right].

Maximum Product Subarray: Given an integer array nums, find a subarray that has the largest product, and return the product. The test cases are generated so that the answer will fit in a 32-bit integer.

DYNAMIC PROGRAMMING: While going through numbers in nums, we will have to keep track of the maximum product up to that number (which we will call max_so_far) and minimum product up to that number (which we will call min_so_far). The reason behind keeping track of max_so_far is to keep track of the accumulated product of positive numbers. The reason behind keeping track of min_so_far is to properly handle negative numbers. max_so_far is updated by taking the maximum value among: 1. Current number: This value will be picked if the accumulated product has been really bad (even compared to the current number). This can happen when the current number has a preceding zero (e.g. [0,4]) or is preceded by a single negative number (e.g. [-3,5]). 2. Product of last max_so_far and current number: This value will be picked if the accumulated product has been steadily increasing (all positive numbers). 3. Product of last min_so_far and current number: This value will be picked if the current number is a negative number and the combo chain has been disrupted by a single negative number before (In a sense, this value is like an antidote to an already poisoned combo chain). min_so_far is updated in using the same three numbers except that we are taking minimum among the above three numbers. Within the for loop, result gets updated each time as the max(max_so_far, result). At the end, return result.

Assign Cookies: Assume you are an awesome parent and want to give your children some cookies. But, you should give each child at most one cookie. Each child i has a greed factor g[i], which is the minimum size of a cookie that the child will be content with; and each cookie j has a size s[j]. If s[j] >= g[i], we can assign the cookie j to the child i, and the child i will be content. Your goal is to maximize the number of your content children and output the maximum number.

GREEDY, TWO POINTER: 1. Sort both the greed array and size arrays. 2. Use pointers for the children and cookies. Use two pointers, incrementing depending on if the size of the cookie is greater than the child greed.

Task Scheduler: Given a characters array tasks, representing the tasks a CPU needs to do, where each letter represents a different task. Tasks could be done in any order. Each task is done in one unit of time. For each unit of time, the CPU could complete either one task or just be idle. However, there is a non-negative integer n that represents the cooldown period between two same tasks (the same letter in the array), that is that there must be at least n units of time between any two same tasks. Return the least number of units of times that the CPU will take to finish all the given tasks.

GREEDY: The total number of CPU intervals we need consists of busy and idle slots. Number of busy slots is defined by the number of tasks to execute: len(tasks). The problem is to compute a number of idle slots. Maximum possible number of idle slots is defined by the frequency of the most frequent task: idle_time <= (f_max - 1) * n. This maximum could be decreased because one doesn't need to keep the CPU idle during cooling periods. It could execute different tasks as well. To compute the minimum number of idle slots, one could use a greedy strategy. The idea is to sort the tasks by frequency in the descending order and fulfill as many idle slots as one could. 1. The maximum number of tasks is 26. Let's allocate an array frequencies of 26 elements to keep the frequency of each task. 2. Iterate over the input array and store the frequency of task A at index 0, the frequency of task B at index 1, etc. 3. Sort the array and retrieve the maximum frequency f_max. This frequency defines the max possible idle time: idle_time = (f_max - 1) * n. 4. Pick the elements in the descending order one by one. At each step, decrease the idle time by min(f_max - 1, f) where f is a current frequency. Remember, that idle_time is greater or equal to 0. 5. Return busy slots + idle slots: len(tasks) + idle_time.

Sum of All Odd Length Subarrays: Given an array of positive integers arr, return the sum of all possible odd-length subarrays of arr. A subarray is a contiguous subsequence of the array.

SLIDING WINDOW: 1. Iterate over all possible odd window sizes (1 - arr.length, odds). 2. Find the initial window sum by summing arr[0] to arr[windowSize - 1]. Add this to the total. 3. Then, move the sliding window (windowSum = windowSum - arr[i - 1] + arr[i + windowSize - 1]) for i from 1 to arr.length - windowSize. 4. Add each iteration sum to the total.

Maximum Average Subarray: You are given an integer array nums consisting of n elements and an integer k. Find a contiguous subarray whose length is equal to k that has the maximum average value and return this value.

SLIDING WINDOW: Since k is constant, we simply need to maximize the sum of the subarray to maximize the average. 1. Construct the first possible subarray of size k and find the sum. 2. Keep updating the sum by adding the next element and subtracting out the first element. 3. Update the max if sum is greater than max. 4. Return max / k.

Longest Substring Without Repeating Characters: Given a string s, find the length of the longest substring without repeating characters.

SLIDING WINDOW: Use a hash set to keep track of characters in current substring. Use left and right pointers to build the sliding window. If the char at right index is not in seen, add the char to the set and increment right. Otherwise, remove the char at the left index from the set and increment left.

Happy Number: Write an algorithm to determine if a number n is happy. A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits. Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy. Return true if n is a happy number, and false if not.

HASHING: Use a HashSet to determine if a cycle has occurred (if the number has already been added to set, return false). Use % and / to calculate the sum of the squares of the digits for the number under question.

Contains Duplicate II: Given an integer array nums and an integer k, return true if there are two distinct indices i and j in the array such that nums[i] == nums[j] and abs(i - j) <= k.

HASHING: Use a hash map to store previous of k elements and their indices. 1. Traverse all elements in the array in a for loop. 2. If a duplicate element is present at distance less than k, return true. 3. Otherwise, set the hash map key of nums[i] = i. 4. If no duplicate is found at the end of the loop, return false.

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

HASHING: 1. Convert both of the arrays to sets. 2. Iterate over the smaller set and check if any elements intersect with the other set.

Contains Duplicates: 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.

HASHING: 1. Create a new hash set. 2. Iterate through the array and check if num is in set. 3. If so, return True. 4. Otherwise, add it to the set. 5. If the loop completes, return False. ---------------------------------------------------------------------- SORTING: If there are any duplicate integers, they will be consecutive after sorting. 1. Sort the array. 2. Sweep the sorted array to find if there are any two consecutive elements.

Can Make Arithmetic Progression From Sequence: A sequence of numbers is called an arithmetic progression if the difference between any two consecutive elements is the same. Given an array of numbers arr, return true if the array can be rearranged to form an arithmetic progression. Otherwise, return false.

HASHING: 1. Find the max and min of arr and compute the average difference. 2. Put all numbers into a hash set. 3. Start from the min, add the average difference to make the next number in the arithmetic sequence, check one by one if it is in the set. 4. If any one is not in the set, return false; otherwise, return true at the end.

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.

HASHING: It turns out we can do it in one-pass. While we are iterating and inserting elements into the hash table, we also look back to check if current element's complement already exists in the hash table. If it exists, we have found a solution and return the indices immediately.

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

HASHING: Iterate through all the cells (rows outer, columns inner) and add three strings to a HashSet. For example, "Value __ found in row i", "Value __ found in column j", and "Value __ found in sub box i/3 - j/3". The add method returns True if it doesn't already exist in the set and False if it does already exist. Make sure that it always returns true, otherwise return false.

Bulls and Cows: You write down a secret number and ask your friend to guess what the number is. When your friend makes a guess, you provide a hint with the following info: - The number of "bulls", which are digits in the guess that are in the correct position. - The number of "cows", which are digits in the guess that are in your secret number but are located in the wrong position. Specifically, the non-bull digits in the guess that could be rearranged such that they become bulls. Given the secret number secret and your friend's guess guess, return the hint for your friend's guess. The hint should be formatted as "xAyB", where x is the number of bulls and y is the number of cows. Note that both secret and guess may contain duplicate digits.

HASHING: Keep a secret array and guess array (each of size 10) for keeping track of the frequency of the digits. Iterate through the array and count the number of bulls. If it is not a bull, increment both the secret array and guess array at the correct digit positions. After the loop, go through the digits array and add the minimum of the frequency of secret array and guess array at that position to cows. Return the formatted string.

Spiral Matrix: Given an m x n matrix, return all elements of the matrix in spiral order.

SIMULATION: The problem statement asks us to return all elements of the matrix in spiral order, which means we will start from the top left corner and move towards right, then down, then left, and then up. We can move the boundaries towards the center of the matrix after we have traversed a row or a column. Then when we meet a boundary, we know it's time to change the direction and update the boundary.

Maximum Subarray of Size K: Given an integer array nums, find the contiguous subarray of size k which has the largest sum and return its sum.

SLIDING WINDOW: 1. First, compute the sum of the first window. 2. Then, slide the window over by adding in the next element and subtracting out the first element. 3. Keep track of the max sum.

Max Consecutive Ones: Given a binary array nums and an integer k, return the maximum number of consecutive 1's in the array if you can flip at most k 0's.

SLIDING WINDOW: 1. If we encounter a 0 then we decrement k. 2. If k < 0 then we need to move the left part of the window forward to try and remove the extra 0's: if the left one was zero then we increment k, regardless of whether we had a 1 or a 0 we can move left side by 1; if we keep seeing 1's the window still keeps moving as-is. 3. Return right - left + 1.

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.

ROTATION: We can iterate over each group of four cells and rotate them. Iterate over half the rows and half the columns (two for loops). temp = matrix[n - 1 - j][i] # Store bottom left matrix[n - 1 - j][i] = matrix[n - 1 - i][n - 1 - j] # Set bottom left matrix[n - 1 - i][n - 1 - j] = matrix[j][n - 1 - i] # Set bottom right matrix[j][n - 1 - i] = matrix[i][j] # Set top right matrix[i][j] = temp # Set top left

Where Will the Ball Fall: You have a 2-D grid of size m x n representing a box, and you have n balls. The box is open on the top and bottom sides. Each cell in the box has a diagonal board spanning two corners of the cell that can redirect a ball to the right or to the left. A board that redirects the ball to the right spans the top-left corner to the bottom-right corner and is represented in the grid as 1. A board that redirects the ball to the left spans the top-right corner to the bottom-left corner and is represented in the grid as -1. We drop one ball at the top of each column of the box. Each ball can get stuck in the box or fall out of the bottom. A ball gets stuck if it hits a "V" shaped pattern between two boards or if a board redirects the ball into either wall of the box. Return an array answer of size n where answer[i] is the column that the ball falls out of at the bottom after dropping the ball from the ith column at the top, or -1 if the ball gets stuck in the box.

SIMULATION: 1. Start by picking up a ball starting from every column col, iterating from the 0th column to the last column. Initialize the current column as col. For every such ball, simulate its movement in every row ranging from the 0th to the nth row. 2. The next column is calculated as: next_column = value of current column + value of current cell. 3. Update the value of next_column in the result array for every row. In the end, the result will store the column number where the ball will fall after the last row.

Reshape the Matrix: In MATLAB, there is a handy function called reshape which can reshape an m x n matrix into a new one with a different size r x c keeping its original data. You are given an m x n matrix mat and two integers r and c representing the number of rows and the number of columns of the wanted reshaped matrix. The reshaped matrix should be filled with all the elements of the original matrix in the same row-traversing order as they were. If the reshape operation with given parameters is possible and legal, output the new reshaped matrix; Otherwise, output the original matrix.

SIMULATION: Do you know how a 2-D array is stored in the main memory (which is 1-D in nature)? It is internally represented as a 1-D array only. The element nums[i][j] of nums array is represented in the form of a one dimensional array by using the index in the form: nums[n∗i+j], where n is the number of columns in the given matrix. Looking at the same in the reverse order, while putting the elements in the elements in the resultant matrix, we can make use of a count variable which gets incremented for every element traversed as if we are putting the elements in a 1-D resultant array. But, to convert the count back into 2-D matrix indices with a column count of c, we can obtain the indices as res[count // c][count %c ] where count // c is the row number and count % c is the column number.

Island Perimeter: You are given row x col grid representing a map where grid[i][j] = 1 represents land and grid[i][j] = 0 represents water. Grid cells are connected horizontally/vertically (not diagonally). The grid is completely surrounded by water, and there is exactly one island (i.e., one or more connected land cells). The island doesn't have "lakes", meaning the water inside isn't connected to the water around the island. One cell is a square with side length 1. Determine the perimeter of the island.

SIMULATION: Iterate through every cell in the grid. If it is land, add 1 to the perimeter for any neighbor that is an edge of the grid or water.

Max Consecutive Ones II: Given a binary array nums, return the maximum number of consecutive 1's in the array if you can flip at most one 0.

SLIDING WINDOW: Use a sliding window and if our sequence is valid, let's continue expanding our sequence (because our goal is to get the largest sequence possible). If our sequence is invalid, let's stop expanding and contract our sequence (because an invalid sequence will never count towards our largest sequence). Let's use left and right pointers to keep track of the current sequence a.k.a. our window. Let's expand our window by moving the right pointer forward until we reach a point where we have more than one 0 in our window. When we reach this invalid state, let's contract our window by moving the left pointer forward until we have a valid window again. By expanding and contracting our window from valid and invalid states, we are able to traverse the array efficiently without repeated overlapping work. Now we can break this approach down into a few actionable steps: while our window is in bounds of the array...add the rightmost element to our window; check if our window is invalid; if so, contract the window until valid; update our the longest sequence we've seen so far; continue to expand our window.

Intersection of Two Arrays II: Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must appear as many times as it shows in both arrays and you may return the result in any order.

SORTING (optimizes space assuming sorting algorithm is O(1) space): 1. Sort both arrays. 2. Use two pointers for each array and add to the list if elements are equal. 3. Else increment the pointer of the value which was smaller. 4. Once one of the pointers reaches len(array), return the list. HASHING (optimizes time): 1. Create a defaultdict(int) which keeps track of the frequency of elements. 2. Iterate through smaller array, incrementing frequency for each element. 3. Then, iterate through the second array, adding to list and decrementing frequency if the frequency is greater than zero in the dict. 4. Return the list. Note: To optimize this approach, use the smaller of the two arrays to construct the dict to save space.

Array Partition I: Given an integer array nums of 2n integers, group these integers into n pairs (a1, b1), (a2, b2), ..., (an, bn) such that the sum of min(ai, bi) for all i is maximized. Return the maximized sum.

SORTING, GREEDY: Suppose x is the smallest possible element in the given list. This means that the contribution to the answer for any pair that includes x must be x, irrespective of the paired element. The other element will essentially be wasted. Hence to minimize our losses, we would like to pair x with the smallest element other than x. 1. We will sort the given list using the built-in sorting function. 2. In the sorted list we will pair the first two elements then the next two elements and so on. Therefore, the first element (at index 0) will be added to the answer max_sum as it is the minimum of the first two elements. Similarly, the third element in the list (at index 2) will be added, and so on. Hence, we will only sum the elements located at the even indices.

3Sum Closest: Given an integer array nums of length n and an integer target, find three integers in nums such that the sum is closest to target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

SORTING, TWO POINTER: Initialize the minimum difference diff with a large value (i.e. float('inf')). Sort the input array nums. Iterate through the array: For the current position i, set low to i + 1, and high to the last index. While the low pointer is smaller than high: sum = nums[i] + nums[low] + nums[high]. If the absolute difference between sum and target is smaller than the absolute value of diff: Set diff to target - sum. If sum is less than target, increment low. Else, decrement high. If diff is zero, break from the loop. Return the value of the closest triplet, which is target - diff.

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.

SORTING, TWO POINTER: Sort the array, iterate through elements (stop at length - 2 because we are finding triplets), and run Two Sum on the remaining elements to the right of the current element in order to find sum = 0 - current_value. Use two pointer method (low and high pointer converging) and skip over duplicate values if you find a successful pair (if there is a match, keep incrementing/decrementing pointers while the next element is the same as the current element).

4Sum: Given an array nums of n integers, return an array of all the unique quadruplets [nums[a], nums[b], nums[c], nums[d]] such that: 0 <= a, b, c, d < n a, b, c, and d are distinct. nums[a] + nums[b] + nums[c] + nums[d] == target You may return the answer in any order.

SORTING, TWO POINTER: The two pointers pattern requires the array to be sorted, so we do that first. Also, it's easier to deal with duplicates if the array is sorted: repeated values are next to each other and easy to skip. For 3Sum, we enumerate each value in a single loop, and use the two pointers pattern for the rest of the array. For kSum, we will have k - 2 nested loops to enumerate all combinations of k - 2 values. We can implement k - 2 loops using a recursion. We will pass the starting point and k as the parameters. When k == 2, we will call 2Sum, terminating the recursion. For the main function: 1. Sort the input array nums. 2. Call kSum with start = 0, k = 4, and target, and return the result. For kSum function: At the start of the kSum function, we will check three conditions: (1) Have we run out of numbers to choose from? (2) Is the smallest number remaining greater than target / k? If so, then any k numbers we choose will be too large. (3) Is the largest number remaining smaller than target / k? If so, then any k numbers we choose will be too small. If any of these conditions is true, there is no need to continue as no combination of the remaining elements can sum to target. If k equals 2, call 2Sum and return the result. Else, iterate i through the array from start: If the current value is the same as the one before, skip it. Recursively call kSum with start = i + 1, k = k - 1, and target - nums[i]. For each returned subset of values: Include the current value nums[i] into subset. Add subset to the result res. Return the result res.

Largest Perimeter Triangle: Given an integer array nums, return the largest perimeter of a triangle with a non-zero area, formed from three of these lengths. If it is impossible to form any triangle of a non-zero area, return 0.

SORTING: Condition a + b > c must be met for valid triangle. 1. Sort the array. 2. Iterate backwards - for any c in the array, we choose the largest possible a and b: these are just the two values adjacent to c. 3. If this forms a triangle, we return the answer.

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.

SORTING: If we sort the intervals by their start value, then each set of intervals that can be merged will appear as a contiguous "run" in the sorted list. 1. First, we sort the list as described (sort(key=lambda x: x[0])). 2. Then, we insert the first interval into our merged list and continue considering each interval in turn. If the current interval begins after the previous interval ends, then they do not overlap and we can append the current interval to merged. Otherwise, they do overlap, and we merge them by updating the end of the previous interval if it is less than the end of the current interval.

Next Greater Element I: The next greater element of some element x in an array is the first greater element that is to the right of x in the same array. You are given two distinct 0-indexed integer arrays nums1 and nums2, where nums1 is a subset of nums2. For each 0 <= i < nums1.length, find the index j such that nums1[i] == nums2[j] and determine the next greater element of nums2[j] in nums2. If there is no next greater element, then the answer for this query is -1. Return an array ans of length nums1.length such that ans[i] is the next greater element as described above.

STACK, HASHING: 1. Use a hash map to map an element to its next greater element. 2. Use a stack to keep a decreasing sub-sequence, whenever we see a number x greater than stack[-1] we pop all elements less than x and for all the popped ones, their next greater element is x. For each num in nums2, while len(stack) > 0 and stack[-1] < num, map[stack.pop()] = num. 3. Outside of the loop, stack.append(num). 4. At the end, overwrite each element of nums1 with map[nums1[i]] if it exists or -1.

Asteroid Collision: We are given an array asteroids of integers representing asteroids in a row. For each asteroid, the absolute value represents its size, and the sign represents its direction (positive meaning right, negative meaning left). Each asteroid moves at the same speed. Find out the state of the asteroids after all collisions. If two asteroids meet, the smaller one will explode. If both are the same size, both will explode. Two asteroids moving in the same direction will never meet.

STACK: A row of asteroids is stable if no further collisions will occur. After adding a new asteroid to the right, some more collisions may happen before it becomes stable again, and all of those collisions (if they happen) must occur right to left. This is the perfect situation for using a stack. Say we have our answer as a stack with rightmost asteroid top, and a new asteroid comes in. If new is moving right (new > 0), or if top is moving left (top < 0), no collision occurs. Otherwise, if abs(new) < abs(top), then the new asteroid will blow up; if abs(new) == abs(top) then both asteroids will blow up; and if abs(new) > abs(top), then the top asteroid will blow up (and possibly more asteroids will, so we should continue checking.)

Sort Colors: Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. You must solve this problem without using the library's sort function.

THREE POINTER: The idea is to move the curr pointer along the array, if nums[curr] = 0 then swap it with nums[p0], if nums[curr] = 2 then swap it with nums[p2]. Initialize the rightmost boundary of zeros: p0 = 0. During the algorithm execution nums[idx < p0] = 0. Initialize the leftmost boundary of twos: p2 = n - 1. During the algorithm execution nums[idx > p2] = 2. Initialise the index of current element to consider: curr = 0. While curr <= p2: If nums[curr] = 0: swap currth and p0th elements and move both pointers to the right. If nums[curr] = 2: swap currth and p2th elements. Move pointer p2 to the left. If nums[curr] = 1: move pointer curr to the right.

Largest Number At Least Twice of Others: You are given an integer array nums where the largest integer is unique. Determine whether the largest element in the array is at least twice as much as every other number in the array. If it is, return the index of the largest element, or return -1 otherwise.

TWO POINTER: 1. Keep track of largest and second largest indices. 2. If you update largest, then update second largest to be the previous largest. 2. Also update second largest where necessary. 4. At the end, check if largest is still at least twice second largest.

Remove Element: Given an integer array nums and an integer val, remove all occurrences of val in nums in-place. The relative order of the elements may be changed. Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements.

TWO POINTER: Since we need to modify the array in-place, we should use a two-pointer approach. The first pointer, j, increments each time and iterates through the array. The second pointer, i, is the index that will be copied next. If the value at j is not equal to the value to remove, copy over the value to index i and increment both. Otherwise, only increment j.

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

TWO POINTER: The idea is to move all non-zero elements to the front of the array and then fill in the zeroes at the end. 1. Iterate through the array. 2. If element is non-zero, write it to the start_pointer spot and increment start_pointer. 3. Else, increment the number of zeroes counter. 4. After this loop, iterate from len(nums) - num_zeroes to the end and fill with zeros.

Minimum Size Subarray Sum: Given an array of positive integers nums and a positive integer target, return the minimal length of a subarray whose sum is greater than or equal to target. If there is no such subarray, return 0 instead.

TWO POINTER: Until now, we have kept the starting index of subarray fixed, and found the last position. Instead, we could move the starting index of the current subarray as soon as we know that no better could be done with this index as the starting index. We could keep 2 pointers, one for the start and another for the end of the current subarray, and make optimal moves so as to keep the sum greater than s as well as maintain the lowest size possible.

Container With Most Water: You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]). Find two lines that together with the x-axis form a container, such that the container contains the most water. Return the maximum amount of water a container can store.

TWO POINTER: Use a pointer for the first container and the last container. On every check, set max_area to be the max of the current area and max_area. If the first pointer amount is less than the last pointer amount, increment the first pointer. If the opposite, decrement the last pointer. Continue loop while first pointer < last pointer (pointers haven't crossed yet).

Remove Duplicates from Sorted Array: Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique element appears only once. The relative order of the elements should be kept the same. Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements. Return k after placing the final result in the first k slots of nums.

TWO POINTER: We need to modify the array in-place and the size of the final array would potentially be smaller than the size of the input array. So, we ought to use a Two Pointer approach here. One that would keep track of the current element in the original array and another one for just the unique elements. Essentially, once an element is encountered, you simply need to bypass its duplicates and move on to the next unique element.


Set pelajaran terkait

AP World History Modern MCQ Prep

View Set

Ch. 17 Activity-Based Costing and Analysis

View Set

First Aid: Responding to Emergencies - Chapter 2: Responding to an Emergency (HE252)

View Set

Bio 223, Chapter 8, The Appendicular Skeleton.

View Set