CS50 Week 5 C — Blood Type Inheritance and Building a Spell Checker with a Hash Table
- Parsa Dev
- Jan 30
- 2 min read

Week 5 of CS50x is where memory management stops being theoretical and starts being the actual job. There are two problems this week — Inheritance and Speller — and between them they cover recursive memory allocation, pointer-based family trees, hash tables, linked lists, and the kind of careful free() discipline that Valgrind will happily punish you for ignoring. Inheritance is the more compact of the two: the program simulates blood type inheritance across three generations of a family. Each person in the tree is a dynamically allocated struct with two parent pointers and two allele characters. The create_family function is recursive — if there are generations left to simulate, it calls itself twice to build both parents before randomly selecting one allele from each to assign to the child. When the oldest generation is reached, parent pointers are set to NULL and alleles are assigned randomly using a random_allele helper that returns 'A', 'B', or 'O'. Freeing the tree is also recursive: free_family walks to the leaves first, freeing each node only after both its parents have already been freed, which is the correct post-order traversal for a binary tree of heap-allocated structs.
Speller is the bigger challenge and the one that really tests your understanding of data structures. The task is to implement a spell checker: load a dictionary of 143,091 words into memory as fast as possible, check each word in a text file against it, and report misspellings along with benchmarked timings for each phase. The whole thing lives in dictionary.c — implementing load, hash, check, size, and unload using a hash table of linked lists. The hash function here uses the second character of each word (case-normalised) to distribute words across 26 buckets, which avoids the naive first-letter approach while keeping the implementation readable. Loading inserts each new node at the head of the appropriate linked list rather than the tail, which is O(1) and avoids any need to traverse the chain. Checking uses strcasecmp for case-insensitive comparison, walking the bucket's linked list until a match is found or the end is reached. The word count is tracked as a global integer incremented on each load, so size is a trivial O(1) return. Unloading walks every bucket and frees every node in each chain, then sets each table pointer back to NULL to leave the structure clean.
What Week 5 teaches above everything else is that memory is not managed for you — you own every byte you allocate, and you are responsible for giving it back. The recursive pattern in Inheritance makes that feel almost elegant; the iterative unloading in Speller makes it feel methodical. Both are worth understanding, because the same discipline applies whether you are writing C, managing heap objects in another language, or thinking about how a runtime's garbage collector works under the hood. Getting Speller right — no leaks, correct output, passing check50 — is one of the more satisfying moments in the course. The full code for this week is on GitHub — browse the Week 5 Dictionary folder directly, or explore the entire CS50x repository to follow the course week by week.




Comments