Leetcode
Find duplicate in array of n+1 numbers [1,n]
#1 sort N log N and then go over it once, O(1) space #2 hashtable go over it once O(N) #3 tortoise and hare cycle algo, have hare move by 2, turtoise moves once each turn (in terms of moving into index value --> index)
in a binary tree, the last row has how many leaves if full
(n+1)/2
middle index of array given start,end
(start+end)/2
left/right child of i in maxHeap
A[2i]/ A[2i + 1]
array index of parent of i in maxHeap
A[i//2]
how to check if tree is BST
Do inorder traversal, if at any point, they are not in order (sorted), not a BST in order: LEFT ROOT RIGHT
Data structures used for DFS and BFS
Stack for DFS, queue for BFS
Rolling hash for string comparison
hash(S)=(∑N−1i=0Si∗pi)%MOD hash(S)=(∑N−1i=0Si∗pi)%MOD Intelligent choices of p and MOD help us avoid collisions. We will try to use p,MOD , where MOD is a prime and p greater than number of distinct elements in our language.
Queue from 2 stacks
same deal: either dequeue expensive or queue empty stack 1 into stack 2, then add node to stack 1, then empty stack 2 in stack 1
Trapping rain water problem
traverse every array element and find the highest bars on left and right sides. Take the smaller of two heights. The difference between smaller height and height of current element is the amount of water that can be stored in this array element, MINUS the element height itself An Efficient Solution is to prre-compute highest bar on left and right of every bar in O(n) time. Then use these pre-computed values to find the amount of water in every array element. Below is C++ implementation of this solution. O(1) Space: move in from both sides, keep highest left and highest right as 2 variables keep going till left index is bigger than right index (lo <= hi) we will calculate water trapped on smaller element out of A[lo] and A[hi] first and move the pointers till lo doesn't cross hi. you pick the smaller of the 2 because if the right max is zero, doesnt matter that left has water (cant be held in)
Split array with positives on 1 side and negatives on the other
use 1 quicksort partition
Daily temperature array, for each element, say the # of days until next warmer temperature
use a stack and travel down arraybackwards for given val at idx i, look at stack, keep popping until you get to a number thats bigger than you. if theres no such number set soln[i] = 0, else set soln at the difference in index between i and the number in the stack then push yourself onto the stack
Utility Class
Class with only static methods
Data Structure for LRU cache
Queue using a doubly linked list and Hash with page number as key and address of queue node as value
Rabin-Karp Algorithm
String searching algorithm uses hashing to find any one of a set of pattern strings in a text. For text of length n and p patterns of combined length m, its average and best case running time is O(n+m) in space O(p), but its worst-case time is O(nm).
Best meeting point A group of two or more people wants to meet and minimize the total travel distance. You are given a 2D grid of values 0 or 1, where each 1 marks the home of someone in the group.
The middle of a given coordinate is the best meeting point in that coordinate Get a list of all (x,y) points with a "1" and keep track of a y_val array and x_val array for points after going through the points, sort the y_val and x_val array. The middle index is the best meeting point then loop through all the points and total up the distance to that point and return O(M*N) complexity
Reverse words in a given string (reverse a sentence)
(I prefer to first do step #2, then step #1) 1) Reverse the individual words, we get the below string. "i ekil siht margorp yrev hcum" 2) Reverse the whole string from start to end and you get the desired output. "much very program this like i"
Implement stack using 2 queues
2 ways: either make push operation costly or pop operation costly Put node in empty q1 (q2 has all nodes), now 1 by 1 dequeue from q2 to queue onto q1, then swap names q1 <-> q2
Buying and Selling Stock (more than once) (without cooldown) The cost of a stock on each day is given in an array, find the max profit that you can make by buying and selling in those days
Add up every interval of valley-->peak move index i till you get to a valley, make that i the valley variable keep moving i till find peak then add peak-valley to sum The key point is we need to consider every peak immediately following a valley to maximize the profit
Adjacency List
Array of linked lists Arr[i] is list of vertices adjacent to vertex i Can just use a dynamic array instead of linked list Lookup can take O(V) use vector in C++ Space is O(V+E), meaning worst case O(V^2)
Merge K sorted lists
CHECK IF LIST IS EMPTY FIRST (return NULL in that case) Divide and conquer on vector of list pointers Make a regular 2-list merge function then iterate with 2 pointers pointing to start and end of vector, and close in from the ends, merging each pair and making front vector Keep a last, left and right pointer istNode* mergeKLists(vector<ListNode*>& lists) { int last = lists.size()-1; while( last != 0){ int left = 0; int right = last; while(left < right){ lists[left] = mergeLists(lists[left],lists[right]); left++; right--; } last = right; } return lists[0]; }
find all possible subtree sums of a binary tree. then output a list of the most common sums (many if ties)
Do POST ORDER traversal using a recursive function (kinda), but FIRST call another main function (that ends up calling the recursive search func) keep track of a global cache map in order to remember the subsums (to use them for the root element sum calculation) loop thru that map later to take care of the max occurences class Solution: def __init__(self): self.cache = {} self.nums = {} def findFrequentTreeSum(self, root): """ :type root: TreeNode :rtype: List[int] """ ret_list = [] if root == None: return ret_list self.Search(root) for key,val in self.cache.items(): if val in self.nums: self.nums[val] += 1 else: self.nums[val] = 1 max_num = 0 for key,val in self.nums.items(): if val > max_num: max_num = val for key,val in self.nums.items(): if val == max_num: ret_list.append(key) return ret_list # does post order traversal left,right,root def Search(self,root): if root.left == None and root.right == None: self.cache[root] = root.val return l_val = 0 r_val = 0 if root.left != None: self.Search(root.left) l_val = self.cache[root.left] if root.right != None: self.Search(root.right) r_val = self.cache[root.right] self.cache[root] = root.val + l_val + r_val
Why Messaging Queue
Enable asynchronous communication via the queue. If the distributed service is flaky and is not always online, you can use a rock solid message queue to persist the messages. The messages are then delivered when the distributed service comes back to life. If the distributed service has low latency and the client service generates more requests than the distributed service can handle, a queue will hold the messages until the distributed service can process them while allowing the client to process uninhibited. If requests need to be distributed to multiple services, an exchange will take care the important details such as delivering the message to each distributed service once and only once.
Pigeonhole sort
Find minimum and maximum values in array. Let the minimum and maximum values be 'min' and 'max' respectively. Also find range as 'max-min-1'. Set up an array of initially empty "pigeonholes" the same size as of the range. Visit each element of the array and then put each element in its pigeonhole. An element arr[i] is put in hole at index arr[i] - min. Start the loop all over the pigeonhole array in order and put the elements from non- empty holes back into the original array.
Find LCA given pointers to root and nodeA, nodeB
First, make 2 vectors, both representing path to nodeA and path to nodeB, then loop through both until you find first point of divergence BUT BETTER SOLUTION BELOW The idea is to traverse the tree starting from root. If any of the given keys (n1 and n2) matches with root, then root is LCA (assuming that both keys are present). If root doesn't match with any of the keys, we recur for left and right subtree. The node which has one key present in its left subtree and the other key present in right subtree is the LCA. If both keys lie in left subtree, then left subtree has LCA also, otherwise LCA lies in right subtree. make the function return a treenode TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if (root == NULL) return NULL; if(root->val == p->val || root->val == q->val){ return root; } TreeNode* l = lowestCommonAncestor(root->left,p,q); TreeNode* r = lowestCommonAncestor(root->right,p,q); if (l != NULL && r != NULL){ return root; } else{ return l ? l:r ; } }
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Floyd's tortoise and hare split into 2 phases: #1 determines if cycle exists, if not, return null otherwise, it uses the intersection point to find the entrance to the cycle #2 We can harness the fact that hare moves twice as quickly as tortoise to assert that when hare and tortoise meet at node h h, hare has traversed twice as many nodes. Using this fact, we deduce the following if they meet at position B, and the start of the cycle is at position A, by the time they meet at position B, and B to A is called C, the hare will have travelled twice as much as turtle, we know that, so we can deduce: 2x (turtle dist) = hare dist 2x (A+B) = (A+B+C+B) 2A + 2b = 2b + A + C A = C so to meet at the start of the cycle, they just have to reset one to zero, the other to A, and then march by one together until they meet.
Given preorder and inorder traversal of a tree, construct the binary tree (optimal)
IDK... uses a stack and a set.. on G4G if you want to look it up
Optimal Substructure
In computer science, a problem is said to have optimal substructure if an optimal solution can be constructed from optimal solutions of its subproblems. This property is used to determine the usefulness of dynamic programming and greedy algorithms for a problem the idea that in order to solve a dynamic optimization problem from some starting period t to some ending period T, one implicitly has to solve subproblems starting from later dates s, where t<s<T. This is an example of optimal substructure. The Principle of Optimality is used to derive the Bellman equation, which shows how the value of the problem starting from t is related to the value of the problem starting from s.
Tiny URL shortener (has to be unique)
Interviewer is not looking for you to take a string, store it to a map by hashing it, etc Hes looking for a scalable,durable solution. Think about API (how the user is going to interact with my server), application layer and persistence layer Draw a block diagram. Persistence layer is where you are going to store keys Load balancer takes HTTP client request and assigns it to a working server. At that level, there also could be a cache for requests Cache layer like memcache or redis Server/worker thread generates url, stores it in persistence layer a-z,A-Z,0-9 is a total of 62 characters Generating tinyURL: Map storage: key as the tiny url, value as the longer url Given a string, generate tinyURL and then check if its in the database, if its not, put it there. PROBLEM: Some other server thread, after checking, could have put that url in. Sln: Have a put if absent method, that has to be supported by the database. Only works for relational? Technique #3: Generate tinyurl, then put long:tiny in DB. ______________ MD5 approach: MD5 of longer url, take first 43 bits of that md5, then use that to generate tiny url. Probability of collision.
Longest Substring with At Most K Distinct Characters
Keep a left index (to signify start of substring) and keep length variable and current letter variable Use hashtable to keep track of letter count, and keep track of a "unique characters" count for the current substring do a rolling method, where you slide/roll the left/length index and keep adding more length if the number of uniques fit in Its pretty straight forward honestly, but it was labeled as hard so idk
In a MxN matrix, that is sorted row rise and column wise (in ascending order), count # of negative numbers
Keep index i and j, starts at the last index of the arrays (-1) and at the top, so at the top right If row a's number at index b is positive, then we know that row b's number at index b must at least be positive too, so we can already start looking at index b-1 Hence, you only move worst case O(m+n)
K Empty Slots
Let days[x] = i be the time that the flower at position x blooms. For each window of k days, let's query the minimum of this window in (amortized) constant time using a MinQueue, a data structure built just for this task. If this minimum is larger than it's two neighbors, then we know this is a place where "k empty slots" occurs, and we record this candidate answer.
Tabulation vs memoization
Memoization: Doesn't attempt to solve all sub problems. Tabulation: Can be done iteratively so less stack space. Memory can be efficiently used , Unnecessary states can be discarded. An additional advantage of the iterative approach is that you are often able to save memory by forgetting the solutions to subproblems you won't need in the future.
Maximum subsequent sum
O(N), using cache DP loop through each number, and fill out DP[i], where DP[i] is the largest sum sequence that ends at this index then loop through all of DP and pick the max How to save time? Only loop once, keep track of a best_max variable and a last_max variable last_max will replace the cache[i-1], to save space and time best_max avoids the loop at the end
Count # of palindromic substrings
O(N^2), for string of length N, every possible palindrome could have 1 of the 2N-1 possible centers Loop through the 2N-1 possible centers (each letters and each in-between letters) and at each, expand outward and count palindromes, until you run into one that is not. At every even numbered center, it counts the actual letter center as a palindrome O(N): Manacher's algorithm (pretty hard) Manacher's algorithm fills in a table P[i] which contains how far the palindrome centered at i extends. If P[5]=3, then three characters on either side of position five are part of the palindrome. The algorithm takes advantage of the fact that if you've found a long palindrome, you can fill in values of P on the right side of the palindrome quickly by looking at the values of P on the left side, since they should mostly be the same. https://stackoverflow.com/questions/10468208/manachers-algorithm-algorithm-to-find-longest-palindrome-substring-in-linear-t
Height of a heap and given i, who is parent, left child, right child
O(log N) Root of tree is A[0]. - Parent of A[i] = A[ i/2 ]. - Left child of A[i] = A[2i]. - Right child of A[i] = A[2i + 1].
Worst case for quicksort
O(n^2) if its sorted ascending and pivot is the first index or if its in descending order and pivot is at the end thus, try picking a random index for pivot
When to use memoization vs tabulation
Recursion with memoization is better whenever the state space is sparse -- in other words, if you don't actually need to solve all smaller subproblems but only some of them. In such cases the recursive implementation can be much faster. Recursion with memoization is also better whenever the state space is irregular, i.e., whenever it is hard to specify an order of evaluation iteratively. Iterative solutions are better whenever the state space is dense and regular. If you need to compute the solutions to all the subproblems anyway, you may as well do it without all the function calling overhead. An additional advantage of the iterative approach is that you are often able to save memory by forgetting the solutions to subproblems you won't need in the future.
Longest increasing subsequence
Related to DAG shortest path problem? Use DP. also O(n^2): For a given index i in the array, computer DP[i], which is the LIS for the sequence up to i (including) How do you compute DP[i] for each? You literally try out every possible jump from every number before i to i (0 to i, 1 to i, 2 to i, 3 to i...) in which case nums will be indexed by j If for a given jump, its a) an increasing jump and b) beats the current record for DP[i], update DP[i] as DP[j] + 1 then take the max of the DP array INITIALIZE DP[i] values to zero For n log n, use list of lists method? Duplicate a list and discard lists depending on if num is greater/smaller than last element of lists
Repeated String Match Given two strings A and B, find the minimum number of times A has to be repeated such that B is a substring of it. If no such solution, return -1. For example, with A = "abcd" and B = "cdabcdab". Return 3, because by repeating A three times ("abcdabcdabcd"), B is a substring of it; and B is not a substring of A repeated two times ("abcdabcd").
Reworded: What is the smallest k such that B is a substring of A*k Find the smallest q for which len(A*q) >= len(B) Now try q and then q+1 Why up to q+1? that q+1 gives A enough repeated length to try every starting combination to fit B into eg. if A = ABC and B = CABCA, in this case ABCABC is not enough since in this case, B starts with the last letter of the pattern, but adding one more: ABCABCABC allows the CABCA to appear BETTER SOLUTION: Use Rabin-Karp algorithm
Given preorder and inorder traversal of a tree, construct the binary tree (suboptimal)
SUBOPTIMAL (also is O(N^2) if tree is left skewed) first element of preorder has to be root of tree, so you look for root in inorder, to the left is the left overall subtree, to the right is the right overall subtree keep track of indexing variables for the in/pre arrays by passing indexes as parameters (in_end and in_start) be careful passing the pre variable, using +1 for left and +2 for right, since +1 might be for the right (assuming empty left) ONLY increment the pre index onto the next root once you have validated your in indexes inhouse (inside the next function call)
Find k smallest items
Sorting O(nlogn) Selection sort O(nk) Max Heap O(k+(n-k)logn)
Remove invalid parentheses from string, enter all valid substrings that have the minimum number of removals. can be more than just one substring
Super important: keep a level variable at any iteration of the queue popping, so that if you find a valid string at a certain level (of letters), you dont go any deeper WITHOUT LEVEL LOCK, you will keep adding valid words that are valid but arent the "minimum valid" BFS over state space of inputed string. first, check if given word is valid if it is, add it to the list if its not, go through the word. if you arrive at a parenthesis, try out a word without it specifically. add that new subword to the queue only if its hasnt been visited yet while char_q.len > 0: wd = char_q.dequeue() if self.checkValid(wd): results.append(wd) continue if level_lock == True: continue i = 0 while i < len(wd): charac = wd[i] if charac == ')' or charac == '(': temp_string = wd[:i] + wd[i+1:] if temp_string not in visited: char_q.enqueue(temp_string) visited[temp_string] = 1 i += 1 return results
Height of a binary tree
The height of a binary tree is the number of edges between the tree's root and its furthest leaf. This means that a tree containing a single node has a height of 0.
Burst balloon problem:
The key idea is, instead of picking the first one to burst, pick the last one. The last one fix the boundary of the both sub-problems so we can divide and conquer. Use dynamic programming For each subarray possible, pick the last balloon to pop to maximize value Can use that info to find max value for entire array Take a 2-D array thats same size as N^2 [i][j] means subarrays that starts at i and end at j store value (pop value, index of pop) First look at subarrays of length of 1
Find two nums that sum to X in an array
To improve our run time complexity, we need a more efficient way to check if the complement exists in the array. If the complement exists, we need to look up its index. In first iteration, add each elements value and its index to table, in the second iteration, we check if each element's complement (target-nums[i]) exists in the table
find middle of a linked list
Traverse linked list using two pointers. Move one pointer by one and other pointer by two. When the fast pointer reaches end slow pointer will reach middle of the linked list.
Greedy algos and optimal substructure
Typically, a greedy algorithm is used to solve a problem with optimal substructure if it can be proved by induction that this is optimal at each step
Iterative inorder traversal (left, root, right) so basically leftmost onwards
USE A STACK 1) Create an empty stack S. 2) Initialize current node as root 3) Push the current node to S and set current = current->left until current is NULL 4) If current is NULL and stack is not empty then a) Pop the top item from stack. b) Print the popped item, set current = popped_item->right c) Go to step 3. 5) If current is NULL and stack is empty then we are done. vector<int> sln; if (root == NULL) return sln; stack <TreeNode*> s; TreeNode* curr = root; int set = 1; while(set == 1){ while(curr != NULL){ s.push(curr); curr = curr->left; } sln.push_back(s.top()->val); curr = s.top()->right; s.pop(); if (curr == NULL && s.empty()) return sln; }
Reverse a string
Use 2 pointers, reverse from both ends on the spot (string is mutable in CPP)
Remove Nth Node From End of List
Use 3 pointers, a,b,c a and b start at head, c starts before head, at NULL let a go ahead by N-1 (so if N=1, a,b should be same block) then keep moving the whole unit together HOLD UP, YOU CAN JUST USE 2 POINTERS, BUT MAKE THEM BOTH START AT NULL (instead of head) and let the first one go first by N+1 if(b == head) head = head->next; else if(b == a) c->next = NULL; else c->next = b->next;
Count number of set bits in number
Use Brian Kernighan's Algorithm Subtraction of 1 from a number toggles all the bits (from right to left) till the rightmost set bit(including the righmost set bit). So if we subtract a number by 1 and do bitwise & with itself (n & (n-1)), we unset the righmost set bit. If we do n & (n-1) in a loop and count the no of times loop executes we get the set bit count. FOR O(1) LOOKUP, USE __builtin_popcount (n) which is a special gcc thing
Longest common Subsequence between 2 input strings
Use DP
Fibonacci number
Use DP iteratively, also use the fact that the power matrix to the power of N times the vector [0 1] gives you the fibonacci number for n You can compute matrix square in log(N) of time but that doesnt mean you get log(N) for fibonacci (because of multiplication) The way out of this apparent paradox is to observe that we can't perform arbitrary-precision arithmetic in constant time
word edit distance (insert, delete, replace)
Use DP. make func that has (word1,word2,m,n) where m is len of 1, n is len of 2 make an [m+1][n+1] cache and iterate in a double for loop of (m*n)
DFS graph
Use a stack to keep track of next node
Find unique element in sorted array
Use binary search for O(log N) 1) Find the middle index, say 'mid'. 2) If 'mid' is even, then compare arr[mid] and arr[mid + 1]. If both are same, then the required element after 'mid' else before mid. 3) If 'mid' is odd, then compare arr[mid] and arr[mid - 1]. If both are same, then the required element after 'mid' else before mid.
BFS graph
Use boolean visited array to avoid duplicate visits. Use a queue to keep track of next node
Print tree in level order
Use queue, print value of curr node, enqueue left and right child, then dequeue whatever is next
How to find substring in doc
Use rabin karp or KMT, rabin karp can have worse case be O(n^2) i think... but KMT worst case is O(n)
Find a pattern of length M in a search string of length N (usually N >> M)
Use rabin-karp using rolling hash comparison by doing modulo of a prime number
Generate all combinations of N pairs of parentheses
Use recursion, try out both adding a closing and a opening bracket, splits into 2, recurse until you reach a bad leaf or if you find a good leaf, at it to the global array solution void genPar(int opens, int closes, string s) { if (opens < 0) return; if (closes < 0) return; if (opens > closes) return; if (closes == 0 and opens == 0){ sln.push_back(s); return; } genPar(opens-1,closes,s+"("); genPar(opens,closes-1,s+")"); }
First unique character in sequence
When building the hash count, make it link to not just the count, but a pair of (count,idx) where idx is first encountered CAN USE AN ARRAY OF {POSSIBLE_CHARS] and also use char to int indexing (for cpp)
Singleton, and is it thread safe?
You need an object that only needs to be instantiate once, so, you can use a singleton. The class needs to declare a private constructor to prevent people to instantiate it from outside the class. Also, you need to declare a static field of the type of the singleton. The method getInstance() assures that only one instance of this class is created at runtime. Singleton not thread safe
Adjacency Matrix
adj[i][j] = 1 indicates that there is an edge from vertex i to vertex j VxV where V is the number of vertices Remove and lookup take O(1) time Even if graph is sparse, O(V^2) space
Binary search tree
binary tree in which the value of each parent node's left child is less than the value the parent node, and the value of the parent node is less than the value of its right child.
Frog crossing river given stones[] array, given position of ith stone If the frog's last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction. Given a list of stones' positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit.
create a hashtable, with (key,value) pair being (stones[i], set<int>) where the set keeps track of the jumpsizes (k's) that come to that stone (to calculate the different k-1,...,k+1 distances) loop through stones, for each stone, look at every jumpsize that goes to it. Try out every of k-1,k,k+1 jumps and check if it leads to a stone (by checking if key exists in map), if it does, add jumpsize to set of that target stone get the answer by asking if hashvalue of last stone is not an empty map, meaning there are jumps possible to it
Next closest time given hr:mn string
create a sorted list of digits, keep track of time as a new list of ints (convert from string) and a position dict. starts at [3], and check if current digit is the max it can take on using the pos dict (if number indexes into a value less than 3, its not the largest possible number), if not, check if the next largest number works. if it doesnt, move on to next index of time (left), if it does, reset any smaller indexes to smallest value they can have
Code for word edit distance
def minD(self,word1,word2,m,n): cache = [ [0 for i in range(n+1)] for x in range(m+1)] for i in range(m+1): for x in range(n+1): if i == 0: cache[i][x] = x elif x == 0: cache[i][x] = i elif word1[i-1] == word2[x-1]: cache[i][x] = cache[i-1][x-1] else: # insert,remove, replace cache[i][x] = 1 + min( cache[i][x-1], cache[i-1][x],cache[i-1][x-1] ) return cache[m][n]
Calculate XOR from 1 to n.
for base 2 numbers only: n % 2^i = n & (2^i + 1) so n %4 == n & 3
Compare version numbers
https://www.geeksforgeeks.org/compare-two-version-numbers/ yeah.... idk
Maximum Size Subarray Sum Equals k
https://xiaokangstudynotes.com/2017/01/24/maximum-size-subarray-sum-equals-k/ int maxSubArrayLen(vector<int>& nums, int k) { unordered_map <int,int> mapper; int sum = 0; int max_gap = 0; for(int i=0; i < nums.size(); ++i){ sum += nums[i]; if(sum == k){ max_gap = i+1; if(mapper.find(sum) == mapper.end()) mapper[sum] = i; continue; } int diff = sum - k; if (mapper.find(diff) != mapper.end()){ int gap = i-mapper[diff]; if (gap > max_gap) max_gap = gap; } if(mapper.find(sum) == mapper.end()) mapper[sum] = i; } return max_gap; }
Invert binary tree
if root == None: return None temp = root.left root.left = self.invertTree(root.right) root.right = self.invertTree(temp) return root
Complexity to make trie, insert and search
insert and search and delete are both O(m), where m is the length of the word Making a tri is O(m*n), where m is longest word and n is the number of words in the dict
Minimum Cost path given a 2-d array of numbers, can only go right or down, min path from top left to bottom right
keep a 2-d cache. use DP iterative tabulation bottom up for loop (2-d), i, k cache[i][k] = grid[i][k] + min (cache[left] , cache[above])
How is a trie node represented
letter string, array of pointers to children, array is of length of alphabet endOfWord bool var to signify this node is the end of a word in the trie
height of binary tree
number of edges between root and leaf
Time complexity of building a heap
o(n) since heapify (done O(n) times) run time depends on the height of the tree, and most times Heap of size N has at most n/(2^h +1) nodes with height h
Memoization
term describing an optimization technique where you cache previously computed results, and return the cached result when the same computation is needed again.
Find min number of coins to make a sum V
use hashmap, build iteratively from 0 to V watch the -1 return if its needed