Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Skills and conventions for an educational algorithms and data structures repository. Use this skill whenever working on algorithm implementations, data structure code, LeetCode-style problems, graph theory, dynamic programming, or any Java-based educational coding project. Trigger on mentions of: algorithms, data structures, graph theory, sorting, searching, trees, DP, BFS, DFS, linked lists, heaps, segment trees, union-find, or any request to write, refactor, document, or test educational code.
.claude/skills/williamfiset-algorithms-education/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 93% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 93% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 98% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 279% | 0% |
This skill defines the conventions and standards for an educational algorithms repository. The goal is to make every algorithm implementation clear, well-tested, and accessible to learners who may not have deep CS backgrounds.
Goal: Every file should teach, not just implement.
Every public method gets a doc comment that explains:
java/** * Finds the shortest path from a source node to all other nodes * using Bellman-Ford's algorithm. Unlike Dijkstra's, this handles * negative edge weights and detects negative cycles. * * @param graph - adjacency list where graph[i] lists edges from node i * @param start - the source node index * @param n - total number of nodes in the graph * @return dist array where dist[i] = shortest distance from start to i, * or Double.NEGATIVE_INFINITY if node i is in a negative cycle * * Time: O(V * E) — relaxes all edges V-1 times * Space: O(V) — stores distance array */
Comment the why, not the what. Focus on lines where the logic isn't obvious:
java// Relax all edges V-1 times. After V-1 passes, shortest paths // are guaranteed if no negative cycles exist. for (int i = 0; i < n - 1; i++) { for (Edge e : edges) { if (dist[e.from] + e.cost < dist[e.to]) { dist[e.to] = dist[e.from] + e.cost; } } } // If we can still relax an edge after V-1 passes, that node // is reachable from a negative cycle — mark it as -infinity. for (int i = 0; i < n - 1; i++) { for (Edge e : edges) { if (dist[e.from] + e.cost < dist[e.to]) { dist[e.to] = Double.NEGATIVE_INFINITY; } } }
Every file starts with a comment block explaining the algorithm in the file
java/** * Bellman-Ford Shortest Path Algorithm * * Computes single-source shortest paths in a weighted graph. * Handles negative edge weights and detects negative cycles. * * Use cases: * - Graphs with negative weights (where Dijkstra fails) * - Detecting negative cycles (e.g., currency arbitrage) * * Run with: * bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:BellmanFordAdjacencyList * * @see <a href="https://en.wikipedia.org/wiki/Bellman-Ford_algorithm">Wikipedia</a> */
Goal: Every algorithm has tests that prove it works and teach edge cases.
Place tests alongside source files or in a tests/ directory. Name test files to mirror the source: BellmanFord.java → BellmanFordTest.java.
For every algorithm, cover these categories:
Use descriptive names that read like a sentence:
java@Test public void testShortestPathSimpleGraph() { ... } @Test public void testDetectsNegativeCycle() { ... } @Test public void testSingleNodeGraph() { ... } @Test public void testDisconnectedNodes() { ... }
Each test method gets a brief comment explaining what scenario it covers and why that scenario matters:
java/** * Graph with a negative cycle reachable from the source. * Bellman-Ford should mark affected nodes as NEGATIVE_INFINITY. * * 0 --5--> 1 --(-10)--> 2 --3--> 1 * (creates cycle 1→2→1 with net cost -7) */ @Test public void testDetectsNegativeCycle() { // ... test body }
Every code change must be accompanied by:
Goal: Keep the codebase clean without losing educational value.
Remove code that is:
Keep alternative implementations when they teach different approaches:
java// ✓ KEEP — BFS and DFS solutions to the same problem teach different techniques public int[] bfsSolve(int[][] grid) { ... } public int[] dfsSolve(int[][] grid) { ... } // ✓ KEEP — iterative vs recursive shows tradeoffs public int fibRecursive(int n) { ... } public int fibIterative(int n) { ... } // ✗ REMOVE — identical logic, just different variable names public int search_v1(int[] arr, int target) { ... } public int search_v2(int[] arr, int target) { ... }
When keeping alternatives, clearly label them with a comment explaining the educational purpose:
java/** * Recursive implementation of binary search. * Compare with binarySearchIterative() to see the iterative approach. * The iterative version avoids stack overhead for large arrays. */
When refactoring, scan for:
Goal: Uniform style across the entire repository.
Use short, clear variable names. Prefer readability through simplicity:
java// ✓ GOOD — short and clear int n = graph.length; int[] dist = new int[n]; boolean[] vis = new boolean[n]; List<int[]> adj = new ArrayList<>(); Queue<Integer> q = new LinkedList<>(); int src = 0; int dst = n - 1; // ✗ BAD — verbose names that clutter algorithm logic int numberOfNodesInGraph = graph.length; int[] shortestDistanceFromSource = new int[numberOfNodesInGraph]; boolean[] hasNodeBeenVisited = new boolean[numberOfNodesInGraph]; List<int[]> adjacencyListRepresentation = new ArrayList<>(); Queue<Integer> breadthFirstSearchQueue = new LinkedList<>(); int sourceNodeIndex = 0; int destinationNodeIndex = numberOfNodesInGraph - 1;
Common short names (use consistently across the repo):
| Name | Meaning | |--------|-------------------------------| | n | number of elements/nodes | | m | number of edges | | i, j | loop indices | | from, to | graph node endpoints | | cost | edge weight | | dist | distance array | | vis | visited array | | adj | adjacency list | | q | queue | | pq | priority queue | | st | stack | | dp | dynamic programming table | | ans | result/answer | | lo | low pointer/bound | | hi | high pointer/bound | | mid | midpoint | | src | source node | | dst | destination node | | cnt | counter | | sz | size | | cur | current element | | prev | previous element | | next | next element (use nxt if shadowing keyword) |
if (...) {)Always use explicit multiplication and parentheses in Big-O expressions for clarity:
java// ✓ GOOD — explicit and unambiguous // Time: O(n*log(n)) // Time: O(n*log^2(n)) // Time: O(n^2*log(n)) // ✗ BAD — missing multiplication and parentheses // Time: O(n log n) // Time: O(n log^2 n) // Time: O(n^2 log n) // Simple expressions without multiplication are fine as-is // Time: O(n) // Time: O(n^2) // Time: O(log(n)) // Space: O(n)
Always place the body of a for loop on its own line, even for single statements. This improves readability, especially in nested loops:
java// ✗ BAD — body on same line as for for (int j = 0; j < n; j++) augmented[i][j] = matrix[i][j]; // ✓ GOOD — body on its own line for (int j = 0; j < n; j++) augmented[i][j] = matrix[i][j]; // ✓ GOOD — nested for loops, each level on its own line for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) for (int k = 0; k < n; k++) result[i][j] += m1[i][k] * m2[k][j];
Streams hurt readability for learners. Use plain loops instead:
java// ✗ AVOID — streams obscure the logic for beginners int sum = Arrays.stream(arr).filter(x -> x > 0).reduce(0, Integer::sum); // ✓ PREFER — a loop is immediately readable int sum = 0; for (int x : arr) { if (x > 0) sum += x; }
Goal: The simplest correct code teaches the best.
java// ✗ AVOID — deep nesting if (node != null) { if (node.left != null) { if (node.left.val == target) { return true; } } } return false; // ✓ PREFER — early returns keep code flat if (node == null) return false; if (node.left == null) return false; return node.left.val == target;
Arrays.sort(), Collections.swap(),Math.min(), etc. are fine because learners need to know these exist
int[] is clearer than ArrayList<Integer> when the size is known
Goal: Catch bugs proactively whenever touching code.
When modifying any lines of code, actively check for and report:
== vs <=, < vs <= in loop conditionsi+1, i-1 without range checksWhen a bug is found, report it clearly:
🐛 BUG FOUND in BellmanFord.java line 42:
Loop runs `i < n` but should be `i < n - 1`.
The extra iteration incorrectly marks reachable nodes as
being in a negative cycle.
FIX: Change `i < n` to `i < n - 1`Goal: Help learners understand the why behind each algorithm.
Goal: The main java method should be near the bottom of the Java file for consistency throughout the project
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 25,972 | 26,021 | +0% | 1 | 1 | 0% | 5,863 | 9,617 | +64% | 0 | 0 | — |
case-02 | fail→pass | 12,205 | 9,676 | -21% | 1 | 1 | 0% | 3,156 | 5,869 | +86% | 0 | 0 | — |
case-03 | fail→pass | 14,992 | 12,950 | -14% | 1 | 1 | 0% | 3,439 | 6,626 | +93% | 0 | 0 | — |
case-04 | pass→fail | 15,373 | 9,095 | -41% | 1 | 1 | 0% | 3,481 | 5,433 | +56% | 0 | 0 | — |
case-05 | pass→pass | 12,535 | 11,825 | -6% | 1 | 1 | 0% | 2,474 | 5,590 | +126% | 0 | 0 | — |
case-06 | pass→pass | 8,989 | 10,716 | +19% | 1 | 1 | 0% | 2,063 | 5,695 | +176% | 0 | 0 | — |
case-07 | fail→pass | 11,273 | 6,284 | -44% | 1 | 1 | 0% | 2,360 | 4,560 | +93% | 0 | 0 | — |
case-08 | fail→pass | 10,482 | 4,558 | -57% | 1 | 1 | 0% | 2,173 | 4,311 | +98% | 0 | 0 | — |
case-09 | fail→pass | 5,674 | 5,484 | -3% | 1 | 1 | 0% | 1,217 | 4,610 | +279% | 0 | 0 | — |
case-10 | pass→pass | 8,357 | 8,822 | +6% | 1 | 1 | 0% | 1,624 | 5,507 | +239% | 0 | 0 | — |
case-11 | fail→fail | 8,578 | 4,409 | -49% | 1 | 1 | 0% | 1,763 | 4,372 | +148% | 0 | 0 | — |
case-12 | fail→fail | 11,026 | 11,751 | +7% | 1 | 1 | 0% | 2,331 | 5,775 | +148% | 0 | 0 | — |
case-13 | fail→pass | 8,622 | 6,246 | -28% | 1 | 1 | 0% | 1,727 | 4,613 | +167% | 0 | 0 | — |
case-14 | fail→pass | 5,618 | 4,816 | -14% | 1 | 1 | 0% | 1,211 | 4,470 | +269% | 0 | 0 | — |
case-15 | pass→pass | 11,025 | 5,193 | -53% | 1 | 1 | 0% | 2,514 | 4,453 | +77% | 0 | 0 | — |
case-16 | pass→pass | 7,822 | 34,738 | +344% | 1 | 1 | 0% | 821 | 4,399 | +436% | 0 | 0 | — |
case-17 | pass→pass | 9,030 | 10,373 | +15% | 1 | 1 | 0% | 1,344 | 3,909 | +191% | 0 | 0 | — |
case-18 | fail→pass | 4,821 | 3,965 | -18% | 1 | 1 | 0% | 669 | 4,077 | +509% | 0 | 0 | — |
case-19 | fail→pass | 8,886 | 5,858 | -34% | 1 | 1 | 0% | 1,560 | 4,322 | +177% | 0 | 0 | — |
case-20 | pass→pass | 12,712 | 5,532 | -56% | 1 | 1 | 0% | 1,985 | 4,270 | +115% | 0 | 0 | — |
case-21 | fail→pass | 11,998 | 3,348 | -72% | 1 | 1 | 0% | 897 | 3,726 | +315% | 0 | 0 | — |
case-22 | pass→pass | 9,303 | 2,898 | -69% | 1 | 1 | 0% | 1,479 | 3,863 | +161% | 0 | 0 | — |
case-23 | fail→pass | 5,441 | 4,964 | -9% | 1 | 1 | 0% | 1,048 | 4,315 | +312% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +43 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.