So my master plan for mastering leetcode is
- Building foundational understanding about concepts.
- Identify a maximum of 3-5 questions that I would do on repeat to achieve mastery for a given subject.
- Write down main helper functions that help and assist for hand extremely common edges cases for a given subject. Example: Middle Index for Binary Search, get column for 2D Grid, getting adjacency nodes for 2D grid, doing out of bound checks for 2D grid. etc.
- Have my writings accessible from various platforms and devices, so I can maximize re-learnings from my own and coding from different devices at various times with ease.
- Have a notebook of top 20 questions.
- Not spend too much time on blogging. Just grokking leetcode. Dump in public gists. Find them by searching as such:
user:mfaani #leetcode #array
General Tips
- Read the question well.
- Identify a small sample input to apply your code against
- When leetcoding, try to submit as you make changes to your code. This helps to catch any compiler bug that later becomes difficult to catch. Often things like missing a
{become hard to track. - Don’t get tripped on Character to String conversions
- Bound checks
>vs>=;countvscount - 1. Basically any math operation can cause you an issue - Questions that use both iteration and dfs, may require you to duplicate logic. Example, make sure you’re not processing a visited node again. (Leetcode: number of islands)
- For any operation, function call, ask yourself: what is the type of the left. What is the type on the right. Do they match?
- Generally edges should get processed as default. Example assume edges in a 2D grid are
0. - Improvements should result in:
- Time Complexity improvement. An improvement from O(2N) to O(N) isn’t worth your time
- Reduction of code indentation. That is:
- 4 conditions is usually better than 6 conditions
- 4 sequential conditions is better than 4 indented conditions. My point is: don’t waste time on improving things that don’t get you much benefit.
In real Leetcode. Brute force is triumphs anything. Because that means you have a working solution. Optimizations should usually come after. While optimized solutions are better, often writing them is more difficult. Hence it’s better to write the more easier solution first. Then just mention / talk how it could be improved. Or perhaps just ask your interviewer if they prefer things differently. However you may not have an interviewer if the assessment is done through some automatic tool (Ex: CodeSignal)
Dumpster
String Indexing
- use
index(after)for advancing the index - compare with
< word1.endIdexto do the bound check - This helps you avoid having optional indexes that get created from using the
index(_:offsetBy:limitedBy:)
1768. Merge Strings Alternately
class Solution {
func mergeAlternately(_ word1: String, _ word2: String) -> String {
var i1 = word1.startIndex
var i2 = word2.startIndex
var result = ""
while i1 < word1.endIndex || i2 < word2.endIndex {
if i1 < word1.endIndex {
result += String(word1[i1])
i1 = word1.index(after: i1)
}
if i2 < word2.endIndex {
result += String(word2[i2])
i2 = word2.index(after: i2)
}
}
return result
}
}```
I initially used `max` on endIndex then used `formIndex` which returned optional, but the above solution was far easier / cleaner.
## Grid Helper functions
```swift
struct Point: Hashable {
let x, y: Int
}
extension Point {
var neighbors: [Point] {
return [Point(x, y + 1), Point(x, y - 1), Point(x + 1,y), Point(x - 1, y)]
}
func getAllowedNeighborsForLimits(_ x: Int, _ y: Int) {
neighbors.filter {$0.x =< x && $0.y =< y}
}
}
/*
Imagine a 5 * 5 array and your point is x:1 y:4
You're 2nd column, 5th row or in programmatic words: column 1, row 4
row4 means [point.y] (as how much down),
column1 means [point.x] (as how much to the right)
rows are the first array in a 2D Grid
columns are the second array in a 2D Grid
so
tldr [point.y][point.x] is the correct way of doing things.
*/
extension Array<Array<Int>> {
subscript (point: Point) -> Int {
get {
self[point.y][point.x]
}
set {
self[point.y][point.x] = newValue
}
}