It's been awhile since I've updated this list, and I haven't stopped watching
movies. Adding movies like "The Help", "Three Colours: Red" (and the whole
trilogy for that matter), "The Elephant Man", and others. All worth watching.
Wahoo! I need to remember that the inorder traversal of a BST should be
sorted. This piece of trivia comes in handy for a variety of BST related
problems.
I also think being able to do a {pre,in,post}-order traversal recursively and
iteratively is a skill that I need to develop.
Valid Anagram
This one is a classic: `sorted(a) == sorted(b)`
Group Anagrams
Using product of prime numbers to create a key for anagrams is much faster than
sorting the characters in each word. It is also satisfyingly simple.
Encode and Decode Strings
My initial implementation was clumsy and prone to fail for edge-cases. A more
elegant solution is using something like:
```python
def encode(words):
return "".join("{}:{}".format(len(x), x) for x in words)
```
This is tricky because Python has variable-width integers, so relying on two's
complement to support the sum of negative numbers results in infinite
recursion. I know three ways to combat this:
1. Use Java.
2. Conditionally branch and handle either addition or subtraction accordingly.
3. Use a mask to enforce fixed-width integers in Python.
Looks like "Rotate Image" is the only Matrix problem that remains. It was nice
to learn more about "Backtracking" -- a term I often encounter -- while
attempting to solve "Word Search".
From my current understanding, it is like Brute Force but with
short-circuiting. It also seems quite similar to Depth First Search, and I'm
currently unaware of how DFS and Backtracking differ. I'm hoping to learn more
though.
I did these during my flight from LON->NYC without wifi. I managed to get both
correct on the first attempt although I did not find the *optimal* solution for
"Reorder List". IMO "Reorder List" is the best Linked List question I've seen
because it covers a few essential Linked List tricks.
Looks like I should prioritize the following topics:
- Dynamic Programming
- String
- Graph
Although I'm not sure how common DP questions are in interviews, DP is a useful
dragon to slay IMO.
TeamBlind.com hosts a curated list of DS&As questions from LeetCode.com that the
author claims appropriately samples the topics worth exploring. I'm creating an
offline list so that I can track my progress and work while I'm traveling.
At some point I should document or write a script for how I package Elm projects
with Nix to be deployed on my website. For now, I'm modeling everything after my
previous success LearnPianoChords.
Creating a simple HTTP RESTful API for exposing our `Server.semiprime`
function. It supports some help messages, primitive parsing and error handling,
and singular vs. batch processing of arguments.
For more sophisticated parsing and error-checking, I prefer to use Haskell's
Servant library.
- Clear the boilerplate that `mix` generated
- Consume `Math.factor` to test which inputs are semiprimes
- Cache all inputs that are semiprimes as soon as we discover that they are
- semiprimes
I considered a couple things related to the Cache:
- Could save space by storing all semiprime factors in a tree. This would make
the lookups more expensive. Also because the tree's depth would never exceed
two (because all semiprimes only have two factors), the tree would be quite
broad, and we may not be saving enough space for the trade to be worthwhile. I
might be wrong about that though.
- We could consider pre-computing all semiprimes when we start the app, but
without running some tests firsts, I'm not sure whether or not it's worth the
trouble.
Define a simple in-memory key-value store for our cache.
TL;DR:
- Define `Cache` as a simple state-keeping `Agent`
- Define `Sup`, which starts our Cache process
- Define `App`, which starts our Supervisor process
- Whitelist `App` in `mix.exs`, so that it starts after calling `iex -S mix`
In case I want to package this project with Nix. For now, it's useful to store
this at the project root because it help my Emacs's `project-find-file`
function.
I've subtly been implementing breadth-first traversals in graphs
incorrectly. The change is subtle, but updating `seen` needs to happen
immediately after queuing an item.
The results will remain the same, but the runtimes will differ dramatically. I
didn't notice this until I attempted to complete LeetCode's "count islands"
challenge, and LeetCode rejected my solution because it could not finish before
timing out. After looking at other candidates' solutions and comparing them to
mine, I couldn't see any difference... except for this subtle difference.
This SO answer provides a helpful explanation:
https://stackoverflow.com/questions/45623722/marking-node-as-visited-on-bfs-when-dequeuing
The take-away lesson here is to always call `seen.add(..)` immediately after
enqueuing.
The space cost is O(n). The runtime cost of enqueue is O(1); the runtime cost of
dequeue is O(n). Using the "accounting method", the cost of an item in the
system is O(1). Here's why:
+------------+----------------------------+------+
| enqueue | push onto lhs | O(1) |
+------------+----------------------------+------+
| lhs -> rhs | pop off lhs; push onto rhs | O(1) |
+------------+----------------------------+------+
| dequeue | pop off rhs | O(1) |
+------------+----------------------------+------+
The bottom-up solution run in O(n) time instead of O(2^n) time, which the
recursive solution runs as:
```
def fib(n):
return fib(n - 2) + fib(n - 1)
```
Remember that exponential algorithms are usually recursive algorithms with
multiple sibling calls to itself.
Write a predicate for checking if a linked-list contains a cycle. For additional
practice, I also implemented a function that accepts a linked-list containing a
cycle and returns the first element of that cycle.
I believe the previous solution is invalid. This solution works and it should be
more time and space efficient.
Space-wise our stack grows proportionate to the depth of our tree, which for a
"balanced" BST should be log(n). Doing a BFT on a BST results in memory usage of
n because when we encounter the leaf nodes at the final level in the tree, they
will be 1/2 * n for a balanced BST.