Is the array sorted or partially sorted? If it is, some form of binary search should be possible. This also usually means that the interviewer is looking for a solution that is faster than O(n).
Arrays hold values of the same type at contiguous memory locations. In an array, we're usually concerned about two things - the position/index of an element and the element itself. Different programming languages implement arrays under the hood differently and can affect the time complexity of operations you make to the array. In some languages like Python, JavaScript, Ruby, PHP, the array (or list in Python) size is dynamic and you do not need to have a size defined beforehand when creating the array. As a result, people usually have an easier time using these languages for interviews.
Arrays are among the most common data structures encountered during interviews. Questions which ask about other topics would likely involve arrays/sequences as well. Mastery of array is essential for interviews!
Can you sort the array? Sometimes sorting the array first may significantly simplify the problem. Make sure that the order of array elements do not need to be preserved before attempting a sort.
**Advantages**
For questions where summation or multiplication of a subarray is involved, pre-computation using hashing or a prefix/suffix sum/product might be useful.
- Store multiple elements of the same type with one single variable name
- Accessing elements is fast as long as you have the index, as opposed to [linked lists](./linked-list.md) where you have to traverse from the head.
If you are given a sequence and the interviewer asks for O(1) space, it might be possible to use the array itself as a hash table. For example, if the array only has values from 1 to N, where N is the length of the array, negate the value at that index (minus one) to indicate presence of that number.
**Disadvantages**
Also O(n) doesn't mean you can only traverse the array once. Sometimes traversing the array more than once can help you solve the problem easily.
- Addition and removal of elements into/from the middle an array is slow because they remaining elements need to be shifted to accommodate the new/missing element. An exception to this is if the position to be inserted/removed is at the end of the array.
- For certain languages where the array size is fixed, it cannot alter its size after initialization. if an insertion causes the total number of elements to exceed the size, a new array has to be allocated and the existing elements have to be copied over. The act of creating a new array and transferring elements over takes O(n) time.
## Arrays are sequences
**Definitions**
Are there duplicate values in the array, would it affect the answer?
Common terms you see when doing problems involving arrays:
When using an index to iterate through array elements, be careful not to go out of bounds.
- Subarray - A range of contiguous values within an array.
- Example: given an array `[2, 3, 6, 1, 5, 4]`, `[3, 6, 1]` is a subarray while `[3, 1, 5]` is not a subarray.
- Subsequence - A sequence that can be derived from the given sequence by deleting some or no elements without changing the order of the remaining elements.
- Example: given an array `[2, 3, 6, 1, 5, 4]`, `[3, 1, 5]` is a subsequence but `[3, 5, 1]` is not a subsequence.
Be mindful about slicing or concatenating arrays in your code. Typically, slicing and concatenating arrays require O(n) time. Use start and end indices to demarcate a subarray/range where possible.
## Time complexity
Sometimes you can traverse the array from the right rather than from the left.
| Operation | Big-O | Note |
| --- | --- | --- |
| Access | O(1) | |
| Search | O(n) | |
| Search (sorted array) | O(log(n)) | |
| Insert | O(n) | Insertion would require shifting all the subsequent elements to the right by one and that takes O(n) |
| Insert (at the end) | O(1) | Special case of insertion where no other element needs to be shifted |
| Remove | O(n) | Removal would require shifting all the subsequent elements to the left by one and that takes O(n) |
| Remove (at the end) | O(1) | Special case of removal where no other element needs to be shifted |
Master the [sliding window technique](https://discuss.leetcode.com/topic/30941/here-is-a-10-line-template-that-can-solve-most-substring-problems) that applies to many subarray problems.
## Learning resources
When you are given two arrays to process, it is common to have one index per array (pointer) to traverse/compare the both of them. For example, we use the same approach to merge two sorted arrays.
- Readings
- [Array in Data Structure: What is, Arrays Operations](https://www.guru99.com/array-data-structure.html), Guru99
- Videos
- [Arrays](https://www.coursera.org/lecture/data-structures/arrays-OsBSF), University of California San Diego
## Corner cases
## Corner cases
- Empty sequence
- Empty sequence
- Sequence with 1 or 2 elements
- Sequence with 1 or 2 elements
- Sequence with repeated elements
- Sequence with repeated elements
- Duplicated values in the sequence
## Things to look out for during interviews
- Clarify if there are duplicate values in the array. Would the presence of duplicate values affect the answer? Does it make the question simpler or harder?
- When using an index to iterate through array elements, be careful not to go out of bounds.
- Be mindful about slicing or concatenating arrays in your code. Typically, slicing and concatenating arrays would take O(n) time. Use start and end indices to demarcate a subarray/range where possible.
## Techniques
Note that because both arrays and strings are sequences (a string is an array of characters), most of the techniques here will apply to string problems.
### Sliding window
Master the [sliding window technique](https://discuss.leetcode.com/topic/30941/here-is-a-10-line-template-that-can-solve-most-substring-problems) that applies to many subarray/substring problems. In a sliding window, the two pointers usually move in the same direction will never overtake each other. This ensures that each value is only visited at most twice and the time complexity is still O(n). Examples: [Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/), [Minimum Size Subarray Sum](https://leetcode.com/problems/minimum-size-subarray-sum/), [Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/)
### Two pointers
Two pointers is a more general version of sliding window where the pointers can cross each other and can be on different arrays. Examples: [Sort Colors](https://leetcode.com/problems/sort-colors/), [Palindromic Substrings](https://leetcode.com/problems/palindromic-substrings/)
When you are given two arrays to process, it is common to have one index per array (pointer) to traverse/compare the both of them, incrementing one of the pointers when relevant. For example, we use this approach to merge two sorted arrays. Examples: [Merge Sorted Array](https://leetcode.com/problems/merge-sorted-array/)
### Traversing from the right
Sometimes you can traverse the array starting from the right instead of the conventional approach of from the left. Examples: [Daily Temperatures](https://leetcode.com/problems/daily-temperatures/), [Number of Visible People in a Queue](https://leetcode.com/problems/number-of-visible-people-in-a-queue/)
### Sorting the array
Is the array sorted or partially sorted? If it is, some form of binary search should be possible. This also usually means that the interviewer is looking for a solution that is faster than O(n).
Can you sort the array? Sometimes sorting the array first may significantly simplify the problem. Obviously this would not work if the order of array elements need to be preserved. Examples: [Merge Intervals](https://leetcode.com/problems/merge-intervals/), [Non-overlapping Intervals](https://leetcode.com/problems/non-overlapping-intervals/)
### Precomputation
For questions where summation or multiplication of a subarray is involved, pre-computation using hashing or a prefix/suffix sum/product might be useful. Examples: [Product of Array Except Self](https://leetcode.com/problems/product-of-array-except-self/), [Minimum Size Subarray Sum](https://leetcode.com/problems/minimum-size-subarray-sum/), [LeetCode questions tagged "prefix-sum"](https://leetcode.com/tag/prefix-sum/)
### Index has a hash key
If you are given a sequence and the interviewer asks for O(1) space, it might be possible to use the array itself as a hash table. For example, if the array only has values from 1 to N, where N is the length of the array, negate the value at that index (minus one) to indicate presence of that number. Examples: [First Missing Positive](https://leetcode.com/problems/first-missing-positive/), [Daily Temperatures](https://leetcode.com/problems/daily-temperatures/)
### Traversing the array more than once
This might be obvious, but traversing the array twice/thrice (as long as fewer than n times) is still O(n). Sometimes traversing the array more than once can help you solve the problem while keeping the time complexity to O(n).
- [Bits, Bytes, Building With Binary](https://medium.com/basecs/bits-bytes-building-with-binary-13cb4289aafa)
Knowledge of binary number system and bit manipulation is less important in coding interviews as most Software Engineers do not have to deal with bits, which is more commonly used when dealing with lower level systems and programming languages. They are still asked sometimes, so you should at least still know how to convert a number from decimal form into binary form, and vice versa, in your chosen programming language.
## Notes
Questions involving binary representations and bitwise operations are asked sometimes and you must be absolutely familiar with how to convert a number from decimal form into binary form (and vice versa) in your chosen programming language.
Some helpful utility snippets:
- Test k<sup>th</sup> bit is set: `num & (1 << k) != 0`.
- Set k<sup>th</sup> bit: `num |= (1 << k)`.
- Turn off k<sup>th</sup> bit: `num &= ~(1 << k)`.
- Toggle the k<sup>th</sup> bit: `num ^= (1 << k)`.
- To check if a number is a power of 2, `(num & num - 1) == 0` or `(num & (-num)) == num`.
Questions involving binary representations and bitwise operations are asked sometimes and you must be absolutely familiar with how to convert a number from decimal form into binary form (and vice versa) in your chosen programming language.
Some helpful utility snippets:
| Technique | Code |
| --- | --- |
| Test k<sup>th</sup> bit is set | `num & (1 << k) != 0`. |
| Set k<sup>th</sup> bit | <code>num |= (1 <<k)</code> |
| Turn off k<sup>th</sup> bit | `num &= ~(1 << k)`. |
| Toggle the k<sup>th</sup> bit | `num ^= (1 << k)`. |
| Multiple by 2<sup>k</sup> | `num << k` |
| Divide by 2<sup>k</sup> | `num >> k` |
| Check if a number is a power of 2 | `(num & num - 1) == 0` or `(num & (-num)) == num` |
Dynamic Programming (DP) is usually used to solve optimization problems. The only way to get better at DP is to practice. It takes some amount of practice to be able to recognize that a problem can be solved by DP.
- [Dynamic Programming – 7 Steps to Solve any DP Interview Problem](https://dev.to/nikolaotasevic/dynamic-programming--7-steps-to-solve-any-dp-interview-problem-3870)
- [Dynamic Programming – 7 Steps to Solve any DP Interview Problem](https://dev.to/nikolaotasevic/dynamic-programming--7-steps-to-solve-any-dp-interview-problem-3870)
- [Less Repetition, More Dynamic Programming](https://medium.com/basecs/less-repetition-more-dynamic-programming-43d29830a630)
- [Less Repetition, More Dynamic Programming](https://medium.com/basecs/less-repetition-more-dynamic-programming-43d29830a630), basecs
- [Dynamic Programming](http://www.cs.yale.edu/homes/aspnes/classes/223/notes.html#dynamicProgramming), James Aspnes, Yale University
## Notes
## Techniques
Dynamic Programming (DP) is usually used to solve optimization problems. The only way to get better at DP is to practice. It takes some amount of practice to be able to recognize that a problem can be solved by DP.
Sometimes you do not need to store the whole DP table in memory, the last two values or the last two rows of the matrix will suffice.
Sometimes you do not need to store the whole DP table in memory, the last two values or the last two rows of the matrix will suffice.
## Recommended LeetCode questions
## Recommended questions
- [0/1 Knapsack or Partition Equal Subset Sum](https://leetcode.com/problems/partition-equal-subset-sum/)
- [0/1 Knapsack or Partition Equal Subset Sum](https://leetcode.com/problems/partition-equal-subset-sum/)
When comparing euclidean distance between two pairs of points, using dx<sup>2</sup> + dy<sup>2</sup> is sufficient. It is unnecessary to square root the value.
Geometry is a branch of mathematics that is concerned with properties of space that are related with distance, shape, size, and relative position of figures. Advanced geometry (e.g. 3D geometry) is not taught in most Computer Science courses, so you can expect that you will only be asked on 2D geometry.
In algorithm interviews, geometry is usually not be the focus of the problem (you're not being evaluated on mathematics after all). You typically have to use other algorithms and/or data structures in the problem.
## Corner cases
- Zero values. This always gets people.
## Techniques
### Distance between two points
When comparing the between two points, using dx<sup>2</sup> + dy<sup>2</sup> is sufficient. It is unnecessary to square root the value. Examples: [K Closest Points to Origin](https://leetcode.com/problems/k-closest-points-to-origin/)
### Overlapping circles
To find out if two circles overlap, check that the distance between the two centers of the circles is less than the sum of their radii.
To find out if two circles overlap, check that the distance between the two centers of the circles is less than the sum of their radii.
### Overlapping rectangles
Two rectangles overlap if the following is true:
```py
overlap = rect_a.left <rect_b.rightand\
rect_a.right > rect_b.left and \
rect_a.top > rect_b.bottom and \
rect_a.bottom <rect_b.top
```
Here's a [nice visualization](https://silentmatt.com/rectangle-intersection/). Examples: [Rectangle Overlap](https://leetcode.com/problems/rectangle-overlap/)
## Sample questions
## Sample questions
- You have a plane with lots of rectangles on it, find out how many of them intersect.
- You have a plane with lots of rectangles on it, find out how many of them intersect.
@ -16,6 +44,12 @@ To find out if two circles overlap, check that the distance between the two cent
- Given many points, find k points that are closest to the origin.
- Given many points, find k points that are closest to the origin.
- [From Theory To Practice: Representing Graphs](https://medium.com/basecs/from-theory-to-practice-representing-graphs-cfd782c5be38)
A graph is a structure containing a set of objects (nodes or vertices) where there can be edges between these nodes/vertices. Edges can be directed or undirected and can optionally have values (a weighted graph). Trees are undirected graphs in which any two vertices are connected by exactly one edge and there can be no cycles in the graph.
- [Deep Dive Through A Graph: DFS Traversal](https://medium.com/basecs/deep-dive-through-a-graph-dfs-traversal-8177df5d0f13)
- [Going Broad In A Graph: BFS Traversal](https://medium.com/basecs/going-broad-in-a-graph-bfs-traversal-959bd1a09255)
## Notes
Graphs are commonly used to model relationship between unordered entities, such as
- Friendship between people - Each node is a person and edges between nodes represent that these two people are friends.
- Distances between locations - Each node is a location and the edge between nodes represent that these locations are connected. The value of the edge represent the distance.
Be familiar with the various graph representations, graph search algorithms and their time and space complexities.
Be familiar with the various graph representations, graph search algorithms and their time and space complexities.
**Graph representations**
You can be given a list of edges and tasked to build your own graph from the edges to perform a traversal on. The common graph representations are:
You can be given a list of edges and tasked to build your own graph from the edges to perform a traversal on. The common graph representations are:
- Adjacency matrix.
- Adjacency matrix
- Adjacency list.
- Adjacency list
- Hashmap of hashmaps.
- Hash table of hash tables
Using a hash table of hash table would be the simplest approach during algorithm interviews. It will be rare that you have to use adjacency matrix or list for graph questions during interviews.
In algorithm interviews, graphs are commonly given in the input as 2D matrices where cells are the nodes and each cell can traverse to its adjacent cells (up/down/left/right). Hence it is important that you be familiar with traversing a 2D matrix. When traversing the matrix, always ensure that your current position is within the boundary of the matrix and has not been visited before.
## Time complexity
`|V|` is the number of vertices while `|E|` is the number of edges.
- [From Theory To Practice: Representing Graphs](https://medium.com/basecs/from-theory-to-practice-representing-graphs-cfd782c5be38), basecs
- [Deep Dive Through A Graph: DFS Traversal](https://medium.com/basecs/deep-dive-through-a-graph-dfs-traversal-8177df5d0f13), basecs
- [Going Broad In A Graph: BFS Traversal](https://medium.com/basecs/going-broad-in-a-graph-bfs-traversal-959bd1a09255), basecs
- Additional (only if you have time)
- [Finding The Shortest Path, With A Little Help From Dijkstra](https://medium.com/basecs/finding-the-shortest-path-with-a-little-help-from-dijkstra-613149fbdc8e), basecs
- [Spinning Around In Cycles With Directed Acyclic Graphs](https://medium.com/basecs/spinning-around-in-cycles-with-directed-acyclic-graphs-a233496d4688), basecs
## Corner cases
- Empty graph
- Graph with one or two nodes
- Disjoint graphs
- Graph with cycles
## Things to look out for during interviews
A tree-like diagram could very well be a graph that allows for cycles and a naive recursive solution would not work. In that case you will have to handle cycles and keep a set of visited nodes when traversing.
- A tree-like diagram could very well be a graph that allows for cycles and a naive recursive solution would not work. In that case you will have to handle cycles and keep a set of visited nodes when traversing.
- Ensure you are correctly keeping track of visited nodes and not visiting each node more than once. Otherwise your code could end up in an infinite loop.
- **Almost never** - Bellman-Ford algorithm, Floyd-Warshall algorithm, Prim's algorithm, Kruskal's algorithm. Your interviewer likely don't know them either.
In coding interviews, graphs are commonly represented as 2-D matrices where cells are the nodes and each cell can traverse to its adjacent cells (up/down/left/right). Hence it is important that you be familiar with traversing a 2-D matrix. When traversing the matrix, always ensure that your current position is within the boundary of the matrix and has not been visited before.
### Depth-first search
Depth-first search is a graph traversal algorithm which explores as far as possible along each branch before backtracking. A stack is usually used to keep track of the nodes that are on the current search path. This can be done either by an implicit [recursion](./recursion.md) stack, or an actual [stack](./stack.md) data structure.
A simple template for doing depth-first searches on a matrix goes like this:
A simple template for doing depth-first searches on a matrix goes like this:
```py
```py
def dfs(matrix):
def dfs(matrix):
# Check for an empty graph.
# Check for an empty matrix/graph.
if not matrix:
if not matrix:
return []
return []
@ -50,7 +90,7 @@ def dfs(matrix):
for direction in directions:
for direction in directions:
next_i, next_j = i + direction[0], j + direction[1]
next_i, next_j = i + direction[0], j + direction[1]
if 0 <= next_i <rowsand0<=next_j<cols:
if 0 <= next_i <rowsand0<=next_j<cols:
# Add in your question-specific checks.
# Add in question-specific checks, where relevant.
traverse(next_i, next_j)
traverse(next_i, next_j)
for i in range(rows):
for i in range(rows):
@ -58,13 +98,17 @@ def dfs(matrix):
traverse(i, j)
traverse(i, j)
```
```
A similar template for doing breadth-first searches on the matrix goes like this:
### Breadth-first search
Breadth-first search is a graph traversal algorithm which starts at a node and explores all nodes at the present depth, before moving on to the nodes at the next depth level. A [queue](./queue.md) is usually used to keep track of the nodes that were encountered but not yet explored.
A similar template for doing breadth-first searches on the matrix goes like this. It is important to use double-ended queues and not arrays/Python lists as enqueuing for double-ended queues is O(1) but it's O(n) for arrays.
# Add in question-specific checks, where relevant.
queue.append((next_i, next_j))
queue.append((next_i, next_j))
for i in range(rows):
for i in range(rows):
@ -90,31 +134,65 @@ def bfs(matrix):
traverse(i, j)
traverse(i, j)
```
```
:::note
:::info
While DFS is implemented using recursion in this sample, it could also be implemented iteratively similar to BFS. The key difference between the algorithms lies in the underlying data structure (BFS uses a queue while DFS uses a stack). The `deque` class in Python can function as both a stack and a queue
While DFS is implemented using recursion in this sample, it could also be implemented iteratively similar to BFS. The key difference between the algorithms lies in the underlying data structure (BFS uses a queue while DFS uses a stack). The `deque` class in Python can function as both a stack and a queue.
:::
:::
For additional tips on BFS and DFS, you can refer to this [LeetCode post](https://leetcode.com/problems/pacific-atlantic-water-flow/discuss/90774/Python-solution-with-detailed-explanation)
For additional tips on BFS and DFS, you can refer to this [LeetCode post](https://leetcode.com/problems/pacific-atlantic-water-flow/discuss/90774/Python-solution-with-detailed-explanation)
## Corner cases
### Topological sorting
- Empty graph
A topological sort or topological ordering of a directed graph is a linear ordering of its vertices such that for every directed edge uv from vertex u to vertex v, u comes before v in the ordering. Precisely, a topological sort is a graph traversal in which each node v is visited only after all its dependencies are visited.
- Graph with one or two nodes
- Disjoint graphs
Topological sorting is most commonly used for job scheduling a sequence of jobs or tasks which has dependencies on other jobs/tasks. The jobs are represented by vertices, and there is an edge from x to y if job x must be completed before job y can be started.
- Graph with cycles
Another example is taking courses in university where courses have pre-requisites.
## Recommended LeetCode questions
Here's an example where the edges is an array of two-value tuples and the first value depends on the second value.
- [Graph Valid Tree (LeetCode Premium)](https://leetcode.com/problems/graph-valid-tree/)
- Either search
- [Number of Connected Components in an Undirected Graph (LeetCode Premium)](https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/)
- [Pacific Atlantic Water Flow](https://leetcode.com/problems/pacific-atlantic-water-flow/)
- [Number of Connected Components in an Undirected Graph (LeetCode Premium)](https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/)
- [Graph Valid Tree (LeetCode Premium)](https://leetcode.com/problems/graph-valid-tree/)
A hash table (commonly referred to as hash map) a data structure that implements an associative array abstract data type, a structure that can map keys to values. A hash table uses a hash function on an element to compute an index, also called a hash code, into an array of buckets or slots, from which the desired value can be found. During lookup, the key is hashed and the resulting hash indicates where the corresponding value is stored.
Hashing is the most common example of a space-time tradeoff. Instead of linearly searching an array every time to determine if an element is present, which takes O(n) time, we can traverse the array once and hash all the elements into a hash table. Determining if the element is present is a simple matter of hashing the element and seeing if it exists in the hash table, which is O(1) on average.
In the case of hash collisions, there are a number of collision resolution techniques that can be used. You will unlikely be asked about details of collision resolution techniques in interviews,
- **Separate chaining** - A linked list is used for each value, so that it stores all the collided items.
- **Open addressing** - All entry records are stored in the bucket array itself. When a new entry has to be inserted, the buckets are examined, starting with the hashed-to slot and proceeding in some probe sequence, until an unoccupied slot is found
## Implementations
| Language | API |
| --- | --- |
| C++ | [`std::map`](https://docs.microsoft.com/en-us/cpp/standard-library/map) |
| Java | [`java.util.Map`](https://docs.oracle.com/javase/10/docs/api/java/util/Map.html). Use [`java.util.HashMap`](https://docs.oracle.com/javase/10/docs/api/java/util/HashMap.html) or [`java.util.TreeMap`](https://docs.oracle.com/javase/10/docs/api/java/util/TreeMap.html) (preferred) |
| Access | N/A | Accessing not possible as the hash code is not known |
| Search | O(1)\* | |
| Insert | O(1)\* | |
| Remove | O(1)\* | |
_\* This is the average case, but in interviews we only care about the average case for hash tables._
## Learning resources
- Readings
- [Taking Hash Tables Off The Shelf](https://medium.com/basecs/taking-hash-tables-off-the-shelf-139cbf4752f0), basecs
- [Hashing Out Hash Functions](https://medium.com/basecs/hashing-out-hash-functions-ea5dd8beb4dd), basecs
- Videos
- [Core: Hash Tables](https://www.coursera.org/lecture/data-structures-optimizing-performance/core-hash-tables-m7UuP), University of California San Diego
## Sample questions
## Sample questions
- Describe an implementation of a least-used cache, and big-O notation of it.
- Describe an implementation of a least-used cache, and big-O notation of it.
- A question involving an API's integration with hash map where the buckets of hash map are made up of linked lists.
- A question involving an API's integration with hash map where the buckets of hash map are made up of linked lists.
- Implement data structure `Map` storing pairs of integers (key, value) and define following member functions in O(1) runtime: `void insert(key, value)`, `void delete(key)`, `int get(key)`, `int getRandomKey()`. [(Solution)](http://blog.gainlo.co/index.php/2016/08/14/uber-interview-question-map-implementation/)
- [Learning to Love Heaps](https://medium.com/basecs/learning-to-love-heaps-cef2b273a238)
A heap is a specialized tree-based data structure which is a complete tree that satisfies the heap property.
## Notes
- Max heap - In a max heap the value of a node must be greatest among the node values in its entire subtree. The same property must be recursively true for all nodes in the tree.
- Min heap - In a min heap the value of a node must be smallest among the node values in its entire subtree. The same property must be recursively true for all nodes in the tree.
In the context of algorithm interviews, heaps and priority queues can be treated as the same data structure. A heap is a useful data structure when it is necessary to repeatedly remove the object with the highest (or lowest) priority, or when insertions need to be interspersed with removals of the root node.
## Implementations
| Language | API |
| --- | --- |
| C++ | [`std::priority_queue`](https://docs.microsoft.com/en-us/cpp/standard-library/priority-queue-class) |
| Heapify (create a heap out of given array of elements) | O(n) |
## Learning resources
- [Learning to Love Heaps](https://medium.com/basecs/learning-to-love-heaps-cef2b273a238), basecs
- [Heapify All The Things With Heap Sort](https://medium.com/basecs/heapify-all-the-things-with-heap-sort-55ee1c93af82), basecs
- [Heaps](http://www.cs.yale.edu/homes/aspnes/classes/223/notes.html#heaps), James Aspnes, Yale University
## Techniques
### Mention of `k`
If you see a top or lowest _k_ being mentioned in the question, it is usually a signal that a heap can be used to solve the problem, such as in [Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/).
If you see a top or lowest _k_ being mentioned in the question, it is usually a signal that a heap can be used to solve the problem, such as in [Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/).
If you require the top _k_ elements use a Min Heap of size _k_. Iterate through each element, pushing it into the heap. Whenever the heap size exceeds _k_, remove the minimum element, that will guarantee that you have the _k_ largest elements.
If you require the top _k_ elements use a Min Heap of size _k_. Iterate through each element, pushing it into the heap. Whenever the heap size exceeds _k_, remove the minimum element, that will guarantee that you have the _k_ largest elements.
## Recommended LeetCode questions
## Recommended questions
- [Merge K Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/)
- [Merge K Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/)
- [Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/)
- [Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/)
Interval questions are questions where you are given an array of two-element arrays (an interval) and the two values represent a start and an end value. Interval questions are considered part of the array family but they involve some common techniques hence they are extracted out to this special section of their own.
Interval questions are a subset of [array](./array.md) questions where you are given an array of two-element arrays (an interval) and the two values represent a start and an end value. Interval questions are considered part of the array family but they involve some common techniques hence they are extracted out to this special section of their own.
An example interval array: `[[1, 2], [4, 7]]`.
An example interval array: `[[1, 2], [4, 7]]`.
Interval questions can be tricky to those who have not tried them before because of the sheer number of cases to consider when they overlap.
Interval questions can be tricky to those who have not tried them before because of the sheer number of cases to consider when they overlap.
Do clarify with the interviewer whether `[1, 2]` and `[2, 3]` are considered overlapping intervals as it affects how you will write your equality checks.
## Corner cases
- Single interval
- Non-overlapping intervals
- An interval totally consumed within another interval
- Duplicate intervals
A common routine for interval questions is to sort the array of intervals by each interval's starting value.
## Things to look out for during interviews
Be familiar with writing code to check if two intervals overlap and merging two overlapping intervals:
- Do clarify with the interviewer whether `[1, 2]` and `[2, 3]` are considered overlapping intervals as it affects how you will write your equality checks.
## Techniques
### Sort the array of intervals by its starting point
A common routine for interval questions is to sort the array of intervals by each interval's starting value. This step is crucial to solving the [Merge Intervals](https://leetcode.com/problems/merge-intervals/) question.
### Checking if two intervals overlap
Be familiar with writing code to check if two intervals overlap.
```py
```py
def is_overlap(a, b):
def is_overlap(a, b):
return a[0] <b[1]andb[0]<a[1]
return a[0] <b[1]andb[0]<a[1]
```
### Merging two intervals
```py
def merge_overlapping_intervals(a, b):
def merge_overlapping_intervals(a, b):
return [min(a[0], b[0]), max(a[1], b[1])]
return [min(a[0], b[0]), max(a[1], b[1])]
```
```
## Corner cases
## Recommended questions
- Single interval
- Non-overlapping intervals
- An interval totally consumed within another interval
This section dives deep into practical tips for specific topics of algorithms and data structures which appear frequently in coding questions. Many algorithm questions involve techniques that can be applied to questions of similar nature. The more techniques you have in your arsenal, the higher the chances of passing the interview. They may lead you to discover corner cases you might have missed out or even lead you towards the optimal approach!
This section dives deep into practical knowledge and techniques for algorithms and data structures which appear frequently in algorithm interviews. The more techniques you have in your arsenal, the higher the chances of passing the interview. They may lead you to discover corner cases you might have missed out or even lead you towards the optimal approach!
For each topic, study links are recommended to help you master the topic. There is a list of recommended common questions to practice which in my opinion is highly valuable for mastering the core concepts for the topic.
For each topic, a brief introduction is given, along with language-specific libraries to use, time complexities cheatsheet, corner cases, things to look out for during interviews, useful techniques, and recommended resources to help you master the topic. Lastly, there is a list of recommended common questions to practice which in my opinion is highly valuable for mastering the core concepts for the topic.
If you are interested in how data structures are implemented, check out [Lago](https://github.com/yangshun/lago), a Data Structures and Algorithms library for JavaScript. It is pretty much still WIP but I intend to make it into a library that is able to be used in production and also a reference resource for revising Data Structures and Algorithms.
If you are interested in how data structures are implemented, check out [Lago](https://github.com/yangshun/lago), a Data Structures and Algorithms library for JavaScript.
## General tips
## General interview tips
Clarify any assumptions you made subconsciously. Many questions are under-specified on purpose.
Clarify any assumptions you made subconsciously. Many questions are under-specified on purpose.
Like arrays, linked lists are used to represent sequential data. The benefit of linked lists is that insertion and deletion of a node in the list (given its location) is O(1) whereas in arrays the following elements will have to be shifted.
Like arrays, a linked list is used to represent sequential data. It is a linear collection of data elements whose order is not given by their physical placement in memory, as opposed to arrays, where data is stored in sequential blocks of memory. Instead, each element contains an address of the next element. It is a data structure consisting of a collection of nodes which together represent a sequence.
Adding a dummy node at the head and/or tail might help to handle many edge cases where operations have to be performed at the head or the tail. The presence of dummy nodes essentially ensures that operations will never have be done on the head or the tail, thereby removing a lot of headache in writing conditional checks to dealing with null pointers. Be sure to remember to remove them at the end of the operation.
In its most basic form, each node contains: data, and a reference (in other words, a link) to the next node in the sequence.
Sometimes linked lists problem can be solved without additional storage. Try to borrow ideas from reverse a linked list problem.
**Advantages**
For deletion in linked lists, you can either modify the node values or change the node pointers. You might need to keep a reference to the previous element.
Insertion and deletion of a node in the list (given its location) is O(1) whereas in arrays the following elements will have to be shifted.
For partitioning linked lists, create two separate linked lists and join them back together.
**Disadvantages**
Linked lists problems share similarity with array problems, think about how you would do it for an array and try to apply it to a linked list.
Access time is linear because directly accessing elements by its position in the list is not possible (in arrays you can do `arr[4]` for example). You have to traverse from the start.
Two pointer approaches are also common for linked lists. For example:
### Singly linked list
- Getting the k<sup>th</sup> from last node - Have two pointers, where one is k nodes ahead of the other. When the node ahead reaches the end, the other node is k nodes behind
A linked list where each node points to the next node and the last node points to `null`.
- Detecting cycles - Have two pointers, where one pointer increments twice as much as the other, if the two pointers meet, means that there is a cycle
- Getting the middle node - Have two pointers, where one pointer increments twice as much as the other. When the faster node reaches the end of the list, the slower node will be at the middle
### Doubly linked list
A linked list where each node has two pointers, `next` which points to the next node and `prev` which points to the previous node. The `prev` pointer of the first node and the `next` pointer of the last node point to `null`.
### Circular linked list
A singly linked list where the last node points back to the first node.
There is a circular doubly linked list variant where the `prev` pointer of the first node points to the last node and the `next` pointer of the last node points to the first node.
## Implementations
Out of the common languages, only Java provides a linked list implementation. Thankfully it's easy to write your own linked list regardless of language.
| Insert | O(1) | Assumes you have traversed to the insertion position |
| Remove | O(1) | Assumes you have traversed to the node to be removed |
## Learning resources
- Readings
- [What's a Linked List, Anyway? [Part 1]](https://medium.com/basecs/whats-a-linked-list-anyway-part-1-d8b7e6508b9d), basecs
- [What's a Linked List, Anyway? [Part 2]](https://medium.com/basecs/whats-a-linked-list-anyway-part-2-131d96f71996), basecs
- Videos
- [Singly-linked lists](https://www.coursera.org/lecture/data-structures/singly-linked-lists-kHhgK), University of California San Diego
- [Doubly linked lists](https://www.coursera.org/lecture/data-structures/doubly-linked-lists-jpGKD), University of California San Diego
## Common routines
## Common routines
@ -29,16 +67,45 @@ Be familiar with the following routines because many linked list questions make
- Counting the number of nodes in the linked list
- Counting the number of nodes in the linked list
- Reversing a linked list in-place
- Reversing a linked list in-place
- Finding the middle node of the linked list using fast/slow pointers
- Finding the middle node of the linked list using two pointers (fast/slow)
- Merging two lists together
- Merging two linked lists together
## Corner cases
## Corner cases
- Empty linked list (head is `null`)
- Single node
- Single node
- Two nodes
- Two nodes
- Linked list has cycle. **Tip:** Clarify with the interviewer whether there can be a cycle in the list. Usually the answer is no
- Linked list has cycles. **Tip:** Clarify beforehand with the interviewer whether there can be a cycle in the list. Usually the answer is no and you don't have to handle it in the code
## Techniques
### Sentinel/dummy nodes
Adding a sentinel/dummy node at the head and/or tail might help to handle many edge cases where operations have to be performed at the head or the tail. The presence of dummy nodes essentially ensures that operations will never have be done on the head or the tail, thereby removing a lot of headache in writing conditional checks to dealing with null pointers. Be sure to remember to remove them at the end of the operation.
### Two pointers
Two pointer approaches are also common for linked lists. This approach is used for many classic linked list problems.
- Getting the k<sup>th</sup> from last node - Have two pointers, where one is k nodes ahead of the other. When the node ahead reaches the end, the other node is k nodes behind
- Detecting cycles - Have two pointers, where one pointer increments twice as much as the other, if the two pointers meet, means that there is a cycle
- Getting the middle node - Have two pointers, where one pointer increments twice as much as the other. When the faster node reaches the end of the list, the slower node will be at the middle
### Using space
Many linked list problems can be easily solved by creating a new linked list and adding nodes to the new linked list with the final result. However, this takes up extra space and makes the question much less challenging. The interviewer will usually request that you modify the linked list in-place and the solve the problem without additional storage. You can borrow ideas from the [Reverse a Linked List](https://leetcode.com/problems/reverse-linked-list/) problem.
### Elegant modification operations
As mentioned earlier, a linked list's non-sequential nature of memory allows for efficient modification of its contents. Unlike arrays where you can only modify the value at a position, for linked lists you can also modify the `next` pointer in addition to the `value`.
Here are some common operations and how they can be achieved easily:
- Truncate a list - Set the `next` pointer to `null` at the last element
- Swapping values of nodes - Just like arrays, just swap the value of the two nodes, there's no need to swap the `next` pointer
- Combining two lists - attach the head of the second list to the tail of the first list
## Recommended LeetCode questions
## Recommended questions
- [Reverse a Linked List](https://leetcode.com/problems/reverse-linked-list/)
- [Reverse a Linked List](https://leetcode.com/problems/reverse-linked-list/)
- [Detect Cycle in a Linked List](https://leetcode.com/problems/linked-list-cycle/)
- [Detect Cycle in a Linked List](https://leetcode.com/problems/linked-list-cycle/)
If code involves division or modulo, remember to check for division or modulo by 0 case.
Math is a foundational aspect of Computer Science and every programmer and computer scientist needs to have basic mathematical knowledge. Thankfully, for the purpose of coding interviews, there usually won't be that much math involved, but some basic math techniques is helpful to know as you may be asked to implement mathematical operations.
When a question involves "a multiple of a number", perhaps modulo might be useful.
## Things to look out for during interviews
Check for and handle overflow/underflow if you are using a typed language like Java and C++. At the very least, mention that overflow/underflow is possible and ask whether you need to handle it.
- If code involves division or modulo, remember to check for division or modulo by 0 case.
- Check for and handle overflow/underflow if you are using a typed language like Java and C++. At the very least, mention that overflow/underflow is possible and ask whether you need to handle it.
- Consider negative numbers and floating point numbers. This may sound obvious, but under interview pressure, many obvious cases go unnoticed.
Consider negative numbers and floating point numbers. This may sound obvious, but under interview pressure, many obvious cases go unnoticed.
## Common formulas
When dealing with floating point numbers, take note of rounding mistakes. Consider using epsilon comparisons instead of equality checks. E.g. `abs(x - y) <= 10e7` instead of `x == y`).
| | Formula |
| --- | --- |
| Check if a number is even | `num % 2 == 0` |
| Sum of 1 to N | 1 + 2 + ... + (N - 1) + N = (N+1) \* N/2 |
If the question asks to implement an operator such as power, squareroot or division and want it to be faster than O(n), binary search is usually the approach to go.
## Techniques
#### Some common formulas:
### Multiples of a number
- Sum of 1 to N = (n+1) \* n/2
When a question involves "whether a number is a multiple of X", the modulo operator would be useful.
- Sum of GP = 2<sup>0</sup> + 2<sup>1</sup> + 2<sup>2</sup> + 2<sup>3</sup> + ... 2<sup>n</sup> = 2<sup>n+1</sup> - 1
- Permutations of N = N! / (N-K)!
- Combinations of N = N! / (K! \* (N-K)!)
## Recommended LeetCode questions
### Comparing floats
When dealing with floating point numbers, take note of rounding mistakes. Consider using epsilon comparisons instead of equality checks. E.g. `abs(x - y) <= 10e7` instead of `x == y`.
### Fast operators
If the question asks you to implement an operator such as power, square root or division and want it to be faster than O(n), some sort of doubling (fast exponentiation) or halving (binary search) is usually the approach to go. Examples: [Pow(x, n)](https://leetcode.com/problems/powx-n/), [Sqrt(x)](https://leetcode.com/problems/sqrtx/)
A matrix is a 2-dimensional array. Questions involving matrices are usually related to dynamic programming or graph traversal.
A matrix is a 2-dimensional array. Questions involving matrices are usually related to [dynamic programming](./dynamic-programming.md) or [graph](./graph.md) traversal.
For questions involving traversal or dynamic programming, you almost always want to make a copy of the matrix with the same dimensions that is initialized to empty values to store the visited state or dynamic programming table. Be familiar with such a routine:
Matrices can be used to represent graphs where each node is a cell on the matrix which has 4 neighbors (except those cells on the edge and corners). This page will focus on questions which don't use matrix as graphs. Questions which are meant to use the matrix as a graph can be found on the [graph section](./graph.md).
## Corner cases
- Empty matrix. Check that none of the arrays are 0 length
- 1 x 1 matrix
- Matrix with only one row or column
## Techniques
### Creating an empty N x M matrix
For questions involving traversal or dynamic programming, you almost always want to make a copy of the matrix with the same size/dimensions that is initialized to empty values to store the visited state or dynamic programming table. Be familiar with such a routine in your language of choice:
This can be done easily in Python in one line.
```py
# Assumes that the matrix is is non-empty
zero_matrix = [[0 for _ in range(len(matrix[0]))] for _ in range(len(matrix))]
```
Copying a matrix in Python is:
```py
```py
rows, cols = len(matrix), len(matrix[0])
copied_matrix = [row[:] for row in matrix]
copy = [[0 for _ in range(cols)] for _ in range(rows)]
```
```
Many grid-based games can be modeled as a matrix, such as Tic-Tac-Toe, Sudoku, Crossword, Connect 4, Battleship, etc. It is not uncommon to be asked to verify the winning condition of the game. For games like Tic-Tac-Toe, Connect 4 and Crosswords, where verification has to be done vertically and horizontally, one trick is to write code to verify the matrix for the horizontal cells, transpose the matrix and reuse the logic for horizontal verification to verify originally vertical cells (which are now horizontal).
### Transposing a matrix
The transpose of a matrix is found by interchanging its rows into columns or columns into rows.
Many grid-based games can be modeled as a matrix, such as Tic-Tac-Toe, Sudoku, Crossword, Connect 4, Battleship, etc. It is not uncommon to be asked to verify the winning condition of the game. For games like Tic-Tac-Toe, Connect 4 and Crosswords, where verification has to be done vertically and horizontally, one trick is to write code to verify the matrix for the horizontal cells, transpose the matrix, and reuse the logic for horizontal verification to verify originally vertical cells (which are now horizontal).
Transposing a matrix in Python is simply:
Transposing a matrix in Python is simply:
@ -22,18 +47,11 @@ Transposing a matrix in Python is simply:
transposed_matrix = zip(*matrix)
transposed_matrix = zip(*matrix)
```
```
## Corner cases
## Recommended questions
- Empty matrix. Check that none of the arrays are 0 length
- You are given a 7 digit phone number, and you should find all possible letter combinations based on the digit-to-letter mapping on numeric pad and return only the ones that have valid match against a given dictionary of words.
- Give all possible letter combinations from a phone number.
- Generate all subsets of a string.
- Print all possible `N` pairs of balanced parentheses.
- E.g. when `N` is `2`, the function should print `(())` and `()()`.
- E.g. when `N` is `3`, we should get `((()))`, `(()())`, `(())()`, `()(())`, `()()()`.
- Implement a Queue class from scratch with an existing bug, the bug is that it cannot take more than 5 elements.
A queue is a linear collection of elements that are maintained in a sequence and can be modified by the addition of elements at one end of the sequence (**enqueue** operation) and the removal of elements from the other end (**dequeue** operation). Usually, the end of the sequence at which elements are added is called the back, tail, or rear of the queue, and the end at which elements are removed is called the head or front of the queue. As an abstract data type, queues can be implemented using arrays or singly linked lists.
- Implement a Queue using two stacks. You may only use the standard `push()`, `pop()`, and `peek()` operations traditionally available to stacks. You do not need to implement the stack yourself (i.e. an array can be used to simulate a stack).
This behavior is commonly called FIFO (first in, first out). The name "queue" for this type of structure comes from the analogy to people lining up in real life to wait for goods or services.
Breadth-first search is commonly implemented using queues.
## Implementations
| Language | API |
| --- | --- |
| C++ | [`std::queue`](https://docs.microsoft.com/en-us/cpp/standard-library/queue-class) |
- [To Queue Or Not To Queue](https://medium.com/basecs/to-queue-or-not-to-queue-2653bcde5b04), basecs
- Videos
- [Queues](https://www.coursera.org/lecture/data-structures/queues-EShpq), University of California San Diego
## Corner cases
- Empty queue
- Queue with one item
- Queue with two items
## Things to look out for during interviews
Most languages don't have a built in Queue class which to be used, and candidates often use arrays (JavaScript) or lists (Python) as a queue. However, note that the enqueue operation in such a scenario will be O(n) because it requires shifting of all other elements by one. In such cases, you can flag this to the interviewer and say that you assume that there's a queue data structure to use which has an efficient enqueue operation.
## Recommended questions
- [Implement Queue using Stacks](https://leetcode.com/problems/implement-queue-using-stacks)
- [Implement Stack using Queues](https://leetcode.com/problems/implement-queue-using-stacks)
Recursion is useful for permutation, because it generates all combinations and tree-based questions. You should know how to generate all permutations of a sequence as well as how to handle duplicates.
Recursion is a method of solving a computational problem where the solution depends on solutions to smaller instances of the same problem.
Remember to always define a base case so that your recursion will end.
All recursive functions contains two parts:
Recursion implicitly uses a stack. Hence all recursive approaches can be rewritten iteratively using a stack. Beware of cases where the recursion level goes too deep and causes a stack overflow (the default limit in Python is 1000). You may get bonus points for pointing this out to the interviewer. Recursion will never be O(1) space complexity because a stack is involved, unless there is [tail-call optimization](https://stackoverflow.com/questions/310974/what-is-tail-call-optimization) (TCO). Find out if your chosen language supports TCO.
1. A base case (or cases) defined, which defines when the recursion is stopped - otherwise it will go on forever!
1. Breaking down the problem into smaller subproblems and invoking the recursive call
## Recommended LeetCode questions
One of the most common example of recursion is the Fibonacci sequence.
- [Subsets](https://leetcode.com/problems/subsets/) and [Subsets II](https://leetcode.com/problems/subsets-ii/)
Many algorithms relevant in coding interviews make heavy use of recursion - binary search, merge sort, tree traversal, depth-first search, etc. In this article, we focus on questions which use recursion but aren't part of other well known algorithms.
<!-- TODO: Talk about backtracking -->
## Learning resources
- Readings
- [Recursion](https://www.cs.utah.edu/~germain/PPS/Topics/recursion.html), University of Utah
- Videos
- [Tail Recursion](https://www.coursera.org/lecture/programming-languages/tail-recursion-YZic1), University of Washington
## Corner cases
- `n = 0`
- `n = 1`
- Make sure you have enough base cases to cover all possible invocations of the recursive function
## Things to look out for during interviews
- Always remember to always define a base case so that your recursion will end.
- Recursion is useful for permutation, because it generates all combinations and tree-based questions. You should know how to generate all permutations of a sequence as well as how to handle duplicates.
- Recursion implicitly uses a stack. Hence all recursive approaches can be rewritten iteratively using a stack. Beware of cases where the recursion level goes too deep and causes a stack overflow (the default limit in Python is 1000). You may get bonus points for pointing this out to the interviewer. Recursion will never be O(1) space complexity because a stack is involved, unless there is [tail-call optimization](https://stackoverflow.com/questions/310974/what-is-tail-call-optimization) (TCO). Find out if your chosen language supports TCO.
- Number of base cases - In the fibonacci example above, note that one of our recursive calls invoke `fib(n - 2)`. This indicates that you should have 2 base cases defined so that your code covers all possible invocations of the function within the input range. If your recursive function only invokes `fn(n - 1)`, then only one base case is needed
## Techniques
### Memoization
In some cases, you may be computing the result for previously computed inputs. Let's look at the Fibonacci example again. `fib(5)` calls `fib(4)` and `fib(3)`, and `fib(4)` calls `fib(3)` and `fib(2)`. `fib(3)` is being called twice! If the value for `fib(3)` is memoized and used again, that greatly improves the efficiency of the algorithm and the time complexity becomes O(n).
## Recommended questions
- [Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)
Sorting is the act of rearranging elements in a sequence in order, either in numerical or lexicographical order, and either ascending or descending.
A number of basic algorithms run in O(n<sup>2</sup>) and should not be used in interviews. In algorithm interviews, you're unlikely to need to implement any of the sorting algorithms from scratch. Instead you would need to sort the input using your language's default sorting function so that you can use binary searches on them.
On a sorted array of elements, by leveraging on its sorted property, searching can be done on them in faster than O(n) time by using a binary search. Binary search compares the target value with the middle element of the array, which informs the algorithm whether the target value lies in the left half or the right half, and this comparison proceeds on the remaining half until the target is found or the remaining half is empty.
## Time complexity
| Algorithm | Time | Space |
| -------------- | ---------------- | --------- |
| Bubble sort | O(n<sup>2</sup>) | O(1) |
| Insertion sort | O(n<sup>2</sup>) | O(1) |
| Selection sort | O(n<sup>2</sup>) | O(1) |
| Quicksort | O(nlog(n)) | O(log(n)) |
| Mergesort | O(nlog(n)) | O(n) |
| Heapsort | O(nlog(n)) | O(1) |
| Counting sort | O(n + k) | O(k) |
| Radix sort | O(nk) | O(n + k) |
| Algorithm | Big-O |
| ------------- | --------- |
| Binary search | O(log(n)) |
## Learning resources
While you're unlikely to be asked to implement a sorting algorithm from scratch during an interview, it is good to know the various time complexities of the different sorting algorithms.
- Readings
- [Sorting Out The Basics Behind Sorting Algorithms](https://medium.com/basecs/sorting-out-the-basics-behind-sorting-algorithms-b0a032873add), basecs
- [Binary Search](https://www.khanacademy.org/computing/computer-science/algorithms/binary-search/), Khan Academy
- [Bubbling Up With Bubble Sorts](https://medium.com/basecs/bubbling-up-with-bubble-sorts-3df5ac88e592), basecs
- [Inching Towards Insertion Sort](https://medium.com/basecs/inching-towards-insertion-sort-9799274430da), basecs
- [Making Sense of Merge Sort (Part 1)](https://medium.com/basecs/making-sense-of-merge-sort-part-1-49649a143478), basecs
- [Making Sense of Merge Sort (Part 2)](https://medium.com/basecs/making-sense-of-merge-sort-part-2-be8706453209), basecs
- [Pivoting To Understand Quicksort (Part 1)](https://medium.com/basecs/pivoting-to-understand-quicksort-part-1-75178dfb9313), basecs
- [Pivoting To Understand Quicksort (Part 2)](https://medium.com/basecs/pivoting-to-understand-quicksort-part-2-30161aefe1d3), basecs
- [Counting Linearly With Counting Sort](https://medium.com/basecs/counting-linearly-with-counting-sort-cd8516ae09b3), basecs
- [Getting To The Root Of Sorting With Radix Sort](https://medium.com/basecs/getting-to-the-root-of-sorting-with-radix-sort-f8e9240d4224), basecs
## Corner cases
- Empty sequence
- Sequence with one element
- Sequence with two elements
- Sequence containing duplicate elements.
## Things to look out for during interviews
Make sure you know the time and space complexity of the language's default sorting algorithm! The time complexity is almost definitely O(nlog(n))). Bonus points if you can name the sort. In Python, it's [Timsort](https://en.wikipedia.org/wiki/Timsort).
## Techniques
### Sorted inputs
When a given sequence is in a sorted order (be it ascending or descending), using binary search should be one of the first things that come to your mind.
When a given sequence is in a sorted order (be it ascending or descending), using binary search should be one of the first things that come to your mind.
## Sample questions
### Sorting an input that has limited range
- Sorting search results on a page given a certain set of criteria.
[Counting sort](https://en.wikipedia.org/wiki/Counting_sort) is a non-comparison-based sort you can use on numbers where you know the range of values beforehand. Examples: [H-Index](https://leetcode.com/problems/h-index/)
- Sort a list of numbers in which each number is at a distance `K` from its actual position.
- Given an array of integers, sort the array so that all odd indexes are greater than the even indexes.
## Recommended questions
- Given users with locations in a list and a logged-in user with locations, find their travel buddies (people who shared more than half of your locations).
- Search for an element in a sorted and rotated array.
- Implementation of an interpreter for a small language that does multiplication/addition/etc.
A a stack is an abstract data type that supports the operations **push** (insert a new element on the top of the stack) and **pop** (remove and return the most recently added element, the element at the top of the stack). As an abstract data type, stacks can be implemented using arrays or singly linked lists.
- Design a `MinStack` data structure that supports a `min()` operation that returns the minimum value in the stack in O(1) time.
- Write an algorithm to determine if all of the delimiters in an expression are matched and closed.
This behavior is commonly called LIFO (last in, first out). The name "stack" for this type of structure comes from the analogy to a set of physical items stacked on top of each other.
- E.g. `{ac[bb]}`, `[dklf(df(kl))d]{}` and `{[[[]]]}` are matched. But `{3234[fd` and `{df][d}` are not.
Stacks are an important way of supporting nested or recursive function calls and is used to implement depth-first search. Depth-first search can be implemented using recursion or a manual stack.
- Sort a stack in ascending order using an additional stack.
## Implementations
| Language | API |
| --- | --- |
| C++ | [`std::stack`](https://docs.microsoft.com/en-us/cpp/standard-library/stack-class) |
Ask about input character set and case sensitivity. Usually the characters are limited to lowercase Latin characters, for example a to z.
A string is a sequence of characters. Many tips that apply to arrays also apply to strings. You're recommended to read the page on [Arrays](./array.md) before reading this page.
When you need to compare strings where the order isn't important (like anagram), you may consider using a HashMap as a counter. If your language has a built-in Counter class like Python, ask to use that instead.
If you need to keep a counter of characters, a common mistake is to say that the space complexity required for the counter is O(n). The space required for a counter is O(1) not O(n). This is because the upper bound is the range of characters, which is usually a fixed constant of 26. The input set is just lowercase Latin characters.
Common data structures for looking up strings efficiently are
- [Rabin Karp](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) for efficient searching of substring using a rolling hash
- [Rabin Karp](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) for efficient searching of substring using a rolling hash
- [KMP](https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) for efficient searching of substring
- [KMP](https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) for efficient searching of substring
## Strings are sequences
## Time complexity
A string is a sequence of characters. Many tips that apply to arrays also apply to strings.
A strings is an array of characters, so the time complexities of basic string operations will closely resemble that of array operations.
Are there duplicate characters in the string, would it affect the answer?
| Operation | Big-O |
| --------- | ----- |
| Access | O(1) |
| Search | O(n) |
| Insert | O(n) |
| Remove | O(n) |
When using an index to iterate through characters, be careful not to go out of bounds.
### Operations involving another string
Be mindful about slicing or concatenating strings in your code. Typically, slicing and concatenating strings require O(n) time. Use start and end indices to demarcate a substring where possible.
Here we assume the other string is of length m.
| Operation | Big-O | Note |
| --- | --- | --- |
| Find substring | O(n.m) | This is the most naive case. There are more efficient algorithms for string searching such as the [KMP algorithm](https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) |
| Concatenating strings | O(n + m) | |
| Slice | O(m) | |
| Split (by token) | O(m) | |
| Strip (remove leading and trailing whitespaces) | O(n) | |
## Corner cases
Sometimes you can traverse the string from the right rather than from the left.
- Empty string
- String with 1 or 2 characters
- String with repeated characters
- Strings with only distinct characters
Master the [sliding window technique](https://discuss.leetcode.com/topic/30941/here-is-a-10-line-template-that-can-solve-most-substring-problems) that applies to many substring problems.
## Things to look out for during interviews
When you are given two strings to process, it is common to have one index per string (pointer) to traverse/compare the both of them. For example, we use the same approach to merge two sorted arrays.
Ask about input character set and case sensitivity. Usually the characters are limited to lowercase Latin characters, for example a to z.
## Common question topics
## Techniques
Many string questions fall into one of these buckets.
Many string questions fall into one of these buckets.
### Non-repeating Characters
### Counting characters
Often you will need to count the frequency of characters in a string. The most common way of doing that is by using a hash table/map in your language of choice. If your language has a built-in Counter class like Python, ask if you can use that instead.
If you need to keep a counter of characters, a common mistake is to say that the space complexity required for the counter is O(n). The space required for a counter of a string of latin characters is O(1) not O(n). This is because the upper bound is the range of characters, which is usually a fixed constant of 26. The input set is just lowercase Latin characters.
- Use a 26-bit bitmask to indicate which lower case latin characters are inside the string.
#### String of unique characters
A neat trick to count the characters in a string of unique characters is to use a 26-bit bitmask to indicate which lower case latin characters are inside the string.
```py
```py
mask = 0
mask = 0
for c in set(word):
for c in word:
mask |= (1 << (ord(c) - ord('a')))
mask |= (1 << (ord(c) - ord('a')))
```
```
To determine if two strings have common characters, perform & on the two bitmasks. If the result is non-zero, `mask_a & mask_b > 0`, then the two strings have common characters.
To determine if two strings have common characters, perform `&` on the two bitmasks. If the result is non-zero, ie.`mask_a & mask_b > 0`, then the two strings have common characters.
### Anagram
### Anagram
An anagram is word switch or word play. It is the result of re-arranging the letters of a word or phrase to produce a new word or phrase, while using all the original letters only once. In interviews, usually we are only bothered with words without spaces in them.
An anagram is word switch or word play. It is the result of rearranging the letters of a word or phrase to produce a new word or phrase, while using all the original letters only once. In interviews, usually we are only bothered with words without spaces in them.
To determine if two strings are anagrams, there are a few plausible approaches:
To determine if two strings are anagrams, there are a few approaches:
- Sorting both strings should produce the same resulting string. This takes O(nlgn) time and O(lgn) space.
- Sorting both strings should produce the same resulting string. This takes O(n.log(n)) time and O(log(n)) space.
- If we map each character to a prime number and we multiply each mapped number together, anagrams should have the same multiple (prime factor decomposition). This takes O(n) time and O(1) space.
- If we map each character to a prime number and we multiply each mapped number together, anagrams should have the same multiple (prime factor decomposition). This takes O(n) time and O(1) space. Examples: [Group Anagram](https://leetcode.com/problems/group-anagrams/)
- Frequency counting of characters will help to determine if two strings are anagrams. This also takes O(n) time and O(1) space.
- Frequency counting of characters will help to determine if two strings are anagrams. This also takes O(n) time and O(1) space.
### Palindrome
### Palindrome
A palindrome is a word, phrase, number, or other sequence of characters which reads the same backward as forward, such as _madam_ or _racecar_.
A palindrome is a word, phrase, number, or other sequence of characters which reads the same backward as forward, such as `madam` or `racecar`.
Here are ways to determine if a string is a palindrome:
Here are ways to determine if a string is a palindrome:
- Reverse the string and it should be equal to itself.
- Reverse the string and it should be equal to itself.
- Have two pointers at the start and end of the string. Move the pointers inward till they meet. At any point in time, the characters at both pointers should match.
- Have two pointers at the start and end of the string. Move the pointers inward till they meet. At every point in time, the characters at both pointers should match.
The order of characters within the string matters, so HashMaps are usually not helpful.
When a question is about counting the number of palindromes, a common trick is to have two pointers that move outward, away from the middle. Note that palindromes can be even or odd length. For each middle pivot position, you need to check it twice: Once that includes the character and once without the character.
The order of characters within the string matters, so hash tables are usually not helpful.
- For substrings, you can terminate early once there is no match.
When a question is about counting the number of palindromes, a common trick is to have two pointers that move outward, away from the middle. Note that palindromes can be even or odd length. For each middle pivot position, you need to check it twice - once that includes the character and once without the character. This technique is used in [Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/).
- For subsequences, use dynamic programming as there are overlapping subproblems. Check out [this question](https://leetcode.com/problems/longest-palindromic-subsequence/).
## Corner cases
- For substrings, you can terminate early once there is no match
- For subsequences, use dynamic programming as there are overlapping subproblems. Check out [this question](https://leetcode.com/problems/longest-palindromic-subsequence/)
- [Leaf It Up To Binary Trees](https://medium.com/basecs/leaf-it-up-to-binary-trees-11001aaf746d)
A tree is a widely used abstract data type that represents a hierarchical structure with a set of connected nodes. Each node in the tree can be connected to many children, but must be connected to exactly one parent, except for the root node, which has no parent.
## Notes
A tree is an undirected and connected acyclic graph. There are no cycles or loops. Each node can be like the root node of its own subtree, making [recursion](recursion.md) a useful technique for tree traversal.
A tree is an undirected and connected acyclic graph.
For the purpose of interviews, you will usually be asked on binary trees as opposed to ternary (3 children) or N-ary (N children) trees. In this page,we will cover binary trees and binary search trees, which is a special case of binary trees.
Recursion is a common approach for trees. When you notice that the subtree problem can be used to solve the entire problem, try using recursion.
Trees are commonly used to represent hierarchical data, e.g. file systems, JSON, and HTML documents. Do check out the section on [Trie](trie.md), which is an advanced tree used for efficiently storing and searching strings.
When using recursion, always remember to check for the base case, usually where the node is `null`.
## Common terms you need to know
When you are asked to traverse a tree by level, use breadth-first search.
- **Neighbor** - Parent or child of a node
- **Ancestor** - A node reachable by traversing its parent chain
- **Descendant** - A node in the node's subtree
- **Degree** - Number of children of a node
- **Degree** of a tree - Maximum degree of nodes in the tree
- **Distance** - Number of edges along the shortest path between two nodes
- **Level/Depth** - Number of edges along the unique path between a node and the root node
- **Width** - Number of nodes in a level
Sometimes it is possible that your recursive function needs to return two values.
### Binary tree
If the question involves summation of nodes along the way, be sure to check whether nodes can be negative.
Binary means two, so nodes in a binary trees have a maximum of two children.
You should be very familiar with writing pre-order, in-order, and post-order traversal recursively. As an extension, challenge yourself by writing them iteratively. Sometimes interviewers ask candidates for the iterative approach, especially if the candidate finishes writing the recursive approach too quickly.
**Binary tree terms**
- Complete binary tree - A complete binary tree is a binary tree in which every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible.
- Balanced binary tree - A binary tree structure in which the left and right subtrees of every node differ in height by no more than 1.
Given such a tree, these are the results for the various traversals.
- **In-order traversal** - Left -> Root -> Right
- Result: 2, 7, 5, 6, 11, 1, 9, 5, 9
- **Pre-order traversal** - Root -> Left -> Right
- Result: 1, 7, 2, 6, 5, 11, 9, 9, 5
- **Post-order traversal** - Left -> Right -> Root
- Result: 2, 5, 11, 6, 7, 5, 9, 9, 1
Note that in-order traversal of a binary tree is insufficient to uniquely serialize a tree. Pre-order or post-order traversal is also required.
### Binary search tree (BST)
In-order traversal of a BST will give you all elements in order.
Be very familiar with the properties of a BST and validating that a binary tree is a BST. This comes up more often than expected.
When a question involves a BST, the interviewer is usually looking for a solution which runs faster than O(n).
#### Time complexity
Do check out the section on [Trie](trie.md), which is an advanced tree.
| Operation | Big-O |
| --------- | --------- |
| Access | O(log(n)) |
| Search | O(log(n)) |
| Insert | O(log(n)) |
| Remove | O(log(n)) |
Space complexity of traversing balanced trees is O(n) while traversing very skewed trees (which is essentially a linked list) will be O(n).
## Learning resources
- Videos
- [Trees](https://www.coursera.org/lecture/data-structures/trees-95qda), University of California San Diego
- Readings
- [How To Not Be Stumped By Trees](https://medium.com/basecs/how-to-not-be-stumped-by-trees-5f36208f68a7), basecs
- [Leaf It Up To Binary Trees](https://medium.com/basecs/leaf-it-up-to-binary-trees-11001aaf746d), basecs
- Additional (only if you have time)
- [The Little AVL Tree That Could](https://medium.com/basecs/the-little-avl-tree-that-could-86a3cae410c7), basecs
- [Busying Oneself With B-Trees](https://medium.com/basecs/busying-oneself-with-b-trees-78bbf10522e7), basecs
- [Painting Nodes Black With Red-Black Trees](https://medium.com/basecs/painting-nodes-black-with-red-black-trees-60eacb2be9a5), basecs
## Corner cases
## Corner cases
@ -32,33 +87,59 @@ Do check out the section on [Trie](trie.md), which is an advanced tree.
- Two nodes
- Two nodes
- Very skewed tree (like a linked list)
- Very skewed tree (like a linked list)
## Special Trees
## Things to look out for during interviews
### Binary Tree
You should be very familiar with writing pre-order, in-order, and post-order traversal recursively. As an extension, challenge yourself by writing them iteratively. Sometimes interviewers ask candidates for the iterative approach, especially if the candidate finishes writing the recursive approach too quickly.
In-order traversal of a binary tree is insufficient to uniquely serialize a tree. Pre-order or post-order traversal is also required.
## Common routines
### Binary Search Tree (BST)
Be familiar with the following routines because many tree questions make use of one or more of these routines in the solution:
In-order traversal of a BST will give you all elements in order.
- Insert value
- Delete value
- Count number of nodes in tree
- Whether a value is in the tree
- Calculate height of the tree
- Binary search tree
- Determine if is binary search tree
- Get maximum value
- Get minimum value
Be very familiar with the properties of a BST and validating that a binary tree is a BST. This comes up more often than expected.
## Techniques
When a question involves a BST, the interviewer is usually looking for a solution which runs faster than O(n).
### Use recursion
Recursion is the most common approach for traversing trees. When you notice that the subtree problem can be used to solve the entire problem, try using recursion.
When using recursion, always remember to check for the base case, usually where the node is `null`.
Sometimes it is possible that your recursive function needs to return two values.
### Traversing by level
When you are asked to traverse a tree by level, use breadth-first search.
### Summation of nodes
If the question involves summation of nodes along the way, be sure to check whether nodes can be negative.
## Recommended LeetCode questions
## Recommended questions
- [Maximum Depth of Binary Tree](https://leetcode.com/problems/maximum-depth-of-binary-tree/)
- [Binary Tree Level Order Traversal](https://leetcode.com/problems/binary-tree-level-order-traversal/)
- [Binary Tree Maximum Path Sum](https://leetcode.com/problems/binary-tree-maximum-path-sum/)
- [Serialize and Deserialize Binary Tree](https://leetcode.com/problems/serialize-and-deserialize-binary-tree/)
- [Binary Tree Level Order Traversal](https://leetcode.com/problems/binary-tree-level-order-traversal/)
- [Subtree of Another Tree](https://leetcode.com/problems/subtree-of-another-tree/)
- [Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/)
- [Construct Binary Tree from Preorder and Inorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)
- [Binary Tree Right Side View](https://leetcode.com/problems/binary-tree-right-side-view/)
- [Subtree of Another Tree](https://leetcode.com/problems/subtree-of-another-tree/)
- [Kth Smallest Element in a BST](https://leetcode.com/problems/kth-smallest-element-in-a-bst/)
- [Construct Binary Tree from Preorder and Inorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)
- [Lowest Common Ancestor of BST](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/)
- [Serialize and Deserialize Binary Tree](https://leetcode.com/problems/serialize-and-deserialize-binary-tree/)
- Binary search tree
- [Lowest Common Ancestor of a Binary Search Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/)
- [Trying to Understand Tries](https://medium.com/basecs/trying-to-understand-tries-3ec6bede0014)
Tries are special trees (prefix trees) that make searching and storing strings more efficient. Tries have many practical applications, such as conducting searches and providing autocomplete. It is helpful to know these common applications so that you can easily identify when a problem can be efficiently solved using a trie.
Be familiar with implementing from scratch, a `Trie` class and its `add`, `remove` and `search` methods.
Tries are special trees (prefix trees) that make searching and storing strings more efficient. Tries have many practical applications, such as conducting searches and providing autocomplete. It is helpful to know these common applications so that you can easily identify when a problem can be efficiently solved using a trie.
## Time complexity
Sometimes preprocessing a dictionary of words (given in a list) into a trie, will improve the efficiency of searching for a word of length k, among n words. Searching becomes O(k) instead of O(n).
`m` is the length of the string used in the operation.
| Operation | Big-O | Note |
| --------- | ----- | ---- |
| Search | O(m) | |
| Insert | O(m) | |
| Remove | O(m) | |
## Learning resources
Be familiar with implementing, from scratch, a `Trie` class and its `add`, `remove` and `search` methods.
- Readings
- [Trying to Understand Tries](https://medium.com/basecs/trying-to-understand-tries-3ec6bede0014), basecs
- [Compressing Radix Trees Without (Too Many) Tears](https://medium.com/basecs/compressing-radix-trees-without-too-many-tears-a2e658adb9a0), basecs
## Corner cases
- Searching for a string in an empty trie
- Inserting empty strings into a trie
## Techniques
Sometimes preprocessing a dictionary of words (given in a list) into a trie, will improve the efficiency of searching for a word of length k, among n words. Searching becomes O(k) instead of O(n).