learn: solve 3Sum (#15) via guided TDD#230
Conversation
|
Claude finished @pertrai1's task in 1m 41s —— View job Code Review Complete ✅
1. Correctness ✅The solution is algorithmically correct and handles all edge cases properly. Mental trace with Example 1: Edge cases verified:
2. Complexity AnalysisTime Complexity: O(n²) ✅ (Correctly stated)
The comment in Space Complexity:
|
📝 WalkthroughWalkthroughThis PR adds a TypeScript solution for LeetCode "0015 - 3Sum" with a two-pointer implementation, a Vitest test suite, README and POST_MORTEM documentation, and updates Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Test ResultsTests have been executed for this pull request. 📊 Coverage Summary
Check the workflow logs for detailed test output. |
🎯 How This Solution WorksThink of 3Sum like organizing a potluck dinner where three people need to bring dishes that perfectly balance out to zero cost. You've got a guest list with everyone's budget (some can contribute, others need help), and you need to find all possible groups of three where the costs cancel out. The Sorting Magic: First, we line everyone up by their budget from most negative to most positive. This isn't just tidying up—it's the key that unlocks the entire strategy. Once sorted, we know that moving left means "more negative" and moving right means "more positive." The Fixed Guest Strategy: Here's where it gets clever. For each person (let's call them the "anchor"), we ask: "Who else can I pair with to balance things out?" If our anchor has -4, we need two other people whose budgets sum to +4. The Two-Pointer Dance: With the remaining guests (everyone after our anchor), we place one pointer at the start (most negative) and one at the end (most positive). It's like adjusting a seesaw:
The Duplicate Dodge: Here's a gotcha—some guests have the same budget! If we've already checked an anchor with budget -1, we skip the next -1 to avoid finding the same groups twice. It's like saying "we already checked what this budget can do, no need to repeat ourselves." The beauty? We check every fixed element once (n iterations) and for each one, we efficiently scan the remaining elements with two pointers (n operations). No triple loops, no chaos—just elegant O(n²) efficiency. It's like we've turned a potentially overwhelming O(n³) guest-matching problem into something much more manageable by being strategic about who we pair together. |
|
🤖 Gemini Code Review (Model: Here's a review of your 1. Pattern IdentificationThis solution effectively uses the Two Pointers pattern, specifically a variation known as "Fixed Element + Two Pointers".
This pattern is highly effective for problems like k-Sum when the array can be sorted. 2. Complexity Verification
3. Key InsightThe "aha!" moment for 3Sum lies in transforming a seemingly cubic problem (three nested loops for brute force) into a quadratic one by leveraging sorting and the Two Pointers pattern.
4. Edge Case AnalysisYour
5. Learning Points
6. Code QualityYour code is clean, well-structured, and follows good practices:
Minor Improvement:
7. Alternative ApproachesYou correctly identified and analyzed alternative approaches in your
Your chosen |
|
💡 Educational Note - The Nested Loop Pattern Lines 18-39 demonstrate the "Fixed Element + Two Pointers" pattern, which is the key insight for 3Sum: for (let i = 0; i < sortedArray.length; i++) {
// ... skip duplicates ...
let left = i + 1;
let right = sortedArray.length - 1;
while (left < right) {
// two-pointer search
}
}Why this works: We're essentially solving TwoSum n times. For each fixed element at position i, we search the remaining sorted subarray for pairs that sum to -nums[i]. Complexity analysis: The outer loop is O(n), and the inner two-pointer scan is O(n), giving us O(n²) total—much better than the O(n³) brute force approach of checking every possible triplet! |
|
🤖 Gemini Code Review (Model: This is an excellent solution for the 3Sum problem, demonstrating a solid understanding of array manipulation, sorting, and the two-pointer pattern. The Here's a detailed review: 1. Pattern IdentificationThis solution primarily uses the Two Pointers pattern, specifically a variant often called "Fixed Element + Two Pointers" or "N-Sum to (N-1)-Sum reduction".
This pattern is highly effective for problems that involve finding combinations of numbers that sum to a target in a sorted array, as it efficiently explores possible pairs while handling duplicates. 2. Complexity Verification
3. Key InsightThe key insight for solving 3Sum efficiently is a two-fold realization:
4. Edge Case AnalysisThe provided test suite (
Potential improvements/optimizations for edge cases (identified in
5. Learning Points
6. Code QualityThe code quality is very good:
Minor Suggestions for
7. Alternative Approaches
The chosen "Sort + Two Pointers" approach is generally considered the most optimal balance of time and space complexity for this problem. |
|
💡 Educational Note - Duplicate Handling Lines 20-22 handle a subtle but critical requirement: if (i > 0 && sortedArray[i] === sortedArray[i - 1]) {
continue;
}Common misconception: This does NOT prevent triplets with duplicate values (like [-1, -1, 2]). Those are valid! What it actually does: Prevents duplicate triplets in the result. If we've already processed all triplets starting with -1, we skip the next -1 to avoid finding the same triplet arrays again. The sorting advantage: Because the array is sorted, all duplicates are adjacent, making them easy to skip with a simple comparison to the previous element. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e3eb90397
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (sum === 0) { | ||
| result.push([sortedArray[i], sortedArray[left], sortedArray[right]]); | ||
| left++; | ||
| right--; |
There was a problem hiding this comment.
Skip duplicate pairs after a match to avoid repeat triplets
When a zero-sum triplet is found, the code just increments left and decrements right once, but it does not skip over subsequent equal values. On inputs with repeated values (e.g., [-2,0,0,2,2]), this produces duplicate triplets like [-2,0,2] twice, which violates the problem requirement of unique triplets. Consider advancing left/right past duplicates after pushing a match so each unique pair is emitted once.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@leetcode/medium/0015-3sum/3sum.test.ts`:
- Around line 4-43: Add a unit test that ensures duplicate left/right values
don't produce duplicate triplets: in the test suite for threeSum add a case
calling threeSum with nums = [-2, 0, 0, 2, 2] and assert the result equals [[-2,
0, 2]]; this targets the inner-loop dedup logic in the threeSum implementation
(look for the two-pointer section where left/right are advanced and ensure
duplicate-skipping is exercised).
In `@leetcode/medium/0015-3sum/3sum.ts`:
- Line 44: The file contains a stray direct invocation of the threeSum function
(threeSum([-4, -1, -1, 0, 1, 2])) that runs on import; remove this call so the
function is only executed by tests or callers, or alternatively wrap it in a
top-level guard (e.g., an explicit main/if-run block) so imports don't trigger
execution; locate the literal call to threeSum in the module and delete it (or
move it into a non-import-executed demo section).
- Around line 29-32: The three-sum implementation pushes a valid triplet when
sum === 0 but only does left++ and right--, which allows duplicate triplets for
repeated values; inside the block handling sum === 0 in the function (using
sortedArray, left, right, result and loop variable i) add duplicate-skipping
loops: after incrementing left and decrementing right, advance left while left <
right and sortedArray[left] === sortedArray[left - 1], and similarly decrement
right while left < right and sortedArray[right] === sortedArray[right + 1]; also
ensure the outer loop over i skips duplicate starting values by continuing when
sortedArray[i] === sortedArray[i - 1].
In `@leetcode/medium/0015-3sum/POST_MORTEM.md`:
- Around line 103-104: The comment is saying skipping duplicate left/right
values after finding a valid triplet is required for correctness (not just
optimization); update the three-sum implementation (the loop that uses
left/right two-pointer logic over the sorted nums array, e.g., the code around
variables left, right, and i in the threeSum function) so that after pushing a
found triplet you advance left while nums[left] == nums[left-1] and decrement
right while nums[right] == nums[right+1] to avoid emitting identical triplets,
and ensure you also move left++ and right-- after those skips.
🧹 Nitpick comments (2)
leetcode/medium/0015-3sum/3sum.ts (2)
1-12: Several comment inaccuracies worth correcting.A few things in the header comments are misleading:
- Line 4: Says two pointers "reduce nested loops that cause O(n²)" — but the solution itself is still O(n²). The two-pointer technique reduces the brute-force O(n³) to O(n²).
- Line 8: The stated invariant ("total must not go below 0") doesn't describe the actual algorithm invariant. The real invariant is that
left < rightwith the sorted array guaranteeing the search space narrows toward the target sum.- Line 12: Space is stated as O(n²), but the auxiliary space is O(1) excluding output (as correctly noted in POST_MORTEM.md). The output space is O(k) where k = number of triplets.
14-16: Add explicit type annotation toresultand avoid mutating the input array.Per coding guidelines, TypeScript solutions should use explicit type annotations. Also,
nums.sort()mutates the caller's array in-place, which can be surprising.Proposed fix
export function threeSum(nums: number[]): number[][] { - const sortedArray = nums.sort((a, b) => a - b); - const result = []; + const sortedArray = [...nums].sort((a, b) => a - b); + const result: number[][] = [];As per coding guidelines: "Use explicit type annotations for function parameters and return types in TypeScript solutions."
| describe('3Sum', () => { | ||
| it('should find all triplets that sum to zero for basic case', () => { | ||
| const nums = [-1, 0, 1, 2, -1, -4]; | ||
| const result = threeSum(nums); | ||
| expect(result).toEqual([ | ||
| [-1, -1, 2], | ||
| [-1, 0, 1] | ||
| ]); | ||
| }); | ||
|
|
||
| it('should return empty array when no triplets sum to zero', () => { | ||
| const nums = [0, 1, 1]; | ||
| const result = threeSum(nums); | ||
| expect(result).toEqual([]); | ||
| }); | ||
|
|
||
| it('should handle array of all zeros', () => { | ||
| const nums = [0, 0, 0]; | ||
| const result = threeSum(nums); | ||
| expect(result).toEqual([[0, 0, 0]]); | ||
| }); | ||
|
|
||
| it('should return empty array for empty input', () => { | ||
| const nums: number[] = []; | ||
| const result = threeSum(nums); | ||
| expect(result).toEqual([]); | ||
| }); | ||
|
|
||
| it('should return empty array for array with fewer than 3 elements', () => { | ||
| const nums = [1, 2]; | ||
| const result = threeSum(nums); | ||
| expect(result).toEqual([]); | ||
| }); | ||
|
|
||
| it('should return empty array for all positive numbers', () => { | ||
| const nums = [1, 2, 3]; | ||
| const result = threeSum(nums); | ||
| expect(result).toEqual([]); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Add a test case with duplicate pairs to catch missing inner-loop dedup.
The current tests don't exercise the scenario where duplicate left/right values produce duplicate triplets. For example, [-2, 0, 0, 2, 2] should return [[-2, 0, 2]] but the current implementation would return it twice. Adding such a test will expose the bug in the solution.
Proposed additional test case
+ it('should not return duplicate triplets when inner values repeat', () => {
+ const nums = [-2, 0, 0, 2, 2];
+ const result = threeSum(nums);
+ expect(result).toEqual([[-2, 0, 2]]);
+ });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| describe('3Sum', () => { | |
| it('should find all triplets that sum to zero for basic case', () => { | |
| const nums = [-1, 0, 1, 2, -1, -4]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([ | |
| [-1, -1, 2], | |
| [-1, 0, 1] | |
| ]); | |
| }); | |
| it('should return empty array when no triplets sum to zero', () => { | |
| const nums = [0, 1, 1]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([]); | |
| }); | |
| it('should handle array of all zeros', () => { | |
| const nums = [0, 0, 0]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([[0, 0, 0]]); | |
| }); | |
| it('should return empty array for empty input', () => { | |
| const nums: number[] = []; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([]); | |
| }); | |
| it('should return empty array for array with fewer than 3 elements', () => { | |
| const nums = [1, 2]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([]); | |
| }); | |
| it('should return empty array for all positive numbers', () => { | |
| const nums = [1, 2, 3]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([]); | |
| }); | |
| }); | |
| describe('3Sum', () => { | |
| it('should find all triplets that sum to zero for basic case', () => { | |
| const nums = [-1, 0, 1, 2, -1, -4]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([ | |
| [-1, -1, 2], | |
| [-1, 0, 1] | |
| ]); | |
| }); | |
| it('should return empty array when no triplets sum to zero', () => { | |
| const nums = [0, 1, 1]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([]); | |
| }); | |
| it('should handle array of all zeros', () => { | |
| const nums = [0, 0, 0]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([[0, 0, 0]]); | |
| }); | |
| it('should return empty array for empty input', () => { | |
| const nums: number[] = []; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([]); | |
| }); | |
| it('should return empty array for array with fewer than 3 elements', () => { | |
| const nums = [1, 2]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([]); | |
| }); | |
| it('should return empty array for all positive numbers', () => { | |
| const nums = [1, 2, 3]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([]); | |
| }); | |
| it('should not return duplicate triplets when inner values repeat', () => { | |
| const nums = [-2, 0, 0, 2, 2]; | |
| const result = threeSum(nums); | |
| expect(result).toEqual([[-2, 0, 2]]); | |
| }); | |
| }); |
🤖 Prompt for AI Agents
In `@leetcode/medium/0015-3sum/3sum.test.ts` around lines 4 - 43, Add a unit test
that ensures duplicate left/right values don't produce duplicate triplets: in
the test suite for threeSum add a case calling threeSum with nums = [-2, 0, 0,
2, 2] and assert the result equals [[-2, 0, 2]]; this targets the inner-loop
dedup logic in the threeSum implementation (look for the two-pointer section
where left/right are advanced and ensure duplicate-skipping is exercised).
| if (sum === 0) { | ||
| result.push([sortedArray[i], sortedArray[left], sortedArray[right]]); | ||
| left++; | ||
| right--; |
There was a problem hiding this comment.
Bug: Missing duplicate skipping for left/right pointers produces duplicate triplets.
After finding a valid triplet, the code advances left++ and right-- but doesn't skip over consecutive duplicate values. For input [-2, 0, 0, 2, 2], this will push [-2, 0, 2] twice.
Proposed fix
if (sum === 0) {
result.push([sortedArray[i], sortedArray[left], sortedArray[right]]);
- left++;
- right--;
+ // Skip duplicate left values
+ while (left < right && sortedArray[left] === sortedArray[left + 1]) left++;
+ // Skip duplicate right values
+ while (left < right && sortedArray[right] === sortedArray[right - 1]) right--;
+ left++;
+ right--;
} else if (sum < 0) {Based on learnings: "Check for two-pointer technique correctness" and "Verify the solution correctly solves the problem for all test cases, including edge cases."
🤖 Prompt for AI Agents
In `@leetcode/medium/0015-3sum/3sum.ts` around lines 29 - 32, The three-sum
implementation pushes a valid triplet when sum === 0 but only does left++ and
right--, which allows duplicate triplets for repeated values; inside the block
handling sum === 0 in the function (using sortedArray, left, right, result and
loop variable i) add duplicate-skipping loops: after incrementing left and
decrementing right, advance left while left < right and sortedArray[left] ===
sortedArray[left - 1], and similarly decrement right while left < right and
sortedArray[right] === sortedArray[right + 1]; also ensure the outer loop over i
skips duplicate starting values by continuing when sortedArray[i] ===
sortedArray[i - 1].
| return result; | ||
| } | ||
|
|
||
| threeSum([-4, -1, -1, 0, 1, 2]); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Remove the stray direct invocation — likely a debug artifact.
This call executes every time the module is imported (including by tests) and has no side effect besides wasted computation. It could also cause confusion if the module is imported in a production context.
Proposed fix
-
-threeSum([-4, -1, -1, 0, 1, 2]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| threeSum([-4, -1, -1, 0, 1, 2]); |
🤖 Prompt for AI Agents
In `@leetcode/medium/0015-3sum/3sum.ts` at line 44, The file contains a stray
direct invocation of the threeSum function (threeSum([-4, -1, -1, 0, 1, 2]))
that runs on import; remove this call so the function is only executed by tests
or callers, or alternatively wrap it in a top-level guard (e.g., an explicit
main/if-run block) so imports don't trigger execution; locate the literal call
to threeSum in the module and delete it (or move it into a non-import-executed
demo section).
| - ⚠️ **Optimization**: Could skip duplicate `left` and `right` values after finding a match for better performance on arrays with many duplicates | ||
| - ⚠️ **Early termination**: Could break early if `nums[i] > 0` (all remaining numbers are positive, so no triplet can sum to zero) |
There was a problem hiding this comment.
Skipping duplicate left/right is a correctness requirement, not just an optimization.
Line 103 frames this as a performance improvement, but it's actually needed for correctness. Without it, inputs like [-2, 0, 0, 2, 2] will produce [-2, 0, 2] twice in the result, violating the "no duplicate triplets" constraint. This should be moved from "Edge cases missed" to a required fix in the implementation.
🤖 Prompt for AI Agents
In `@leetcode/medium/0015-3sum/POST_MORTEM.md` around lines 103 - 104, The comment
is saying skipping duplicate left/right values after finding a valid triplet is
required for correctness (not just optimization); update the three-sum
implementation (the loop that uses left/right two-pointer logic over the sorted
nums array, e.g., the code around variables left, right, and i in the threeSum
function) so that after pushing a found triplet you advance left while
nums[left] == nums[left-1] and decrement right while nums[right] ==
nums[right+1] to avoid emitting identical triplets, and ensure you also move
left++ and right-- after those skips.
🔧 Bug Fix: Duplicate Triplet PreventionIssue Found: The original implementation was returning duplicate triplets for arrays with many duplicate values (e.g., Root Cause: We were only skipping duplicate values for the fixed element Fix Applied: Added duplicate skipping for both if (sum === 0) {
result.push([sortedArray[i], sortedArray[left], sortedArray[right]]);
left++;
right--;
// Skip duplicate values for left pointer
while (left < right && sortedArray[left] === sortedArray[left - 1]) {
left++;
}
// Skip duplicate values for right pointer
while (left < right && sortedArray[right] === sortedArray[right + 1]) {
right--;
}
}Result: All 7 tests now pass, including the new edge case test for arrays with many duplicate values. ✅ |
|
Claude finished @pertrai1's task in 2m 5s —— View job Code Review Complete ✅Reviewing your 3Sum solution as a learning exercise... Review Checklist:
1. Correctness ✅Your solution is algorithmically correct after the duplicate-skipping fix (commit ff357cc). The implementation now properly handles all edge cases. Let me trace through Example 1 to verify: Edge cases verified:
Great catch on the duplicate bug! Your fix in commit ff357cc properly addresses the issue identified by other reviewers by adding inner-loop duplicate skipping (lines 34-40). 2. Complexity AnalysisTime Complexity: O(n²) ✅ AccurateYour analysis in
The comment in Space Complexity:
|
|
🤖 Gemini Code Review (Model: Here's a detailed review of the 1. Pattern IdentificationThis solution primarily uses the Two Pointers pattern, specifically a variant often called "Fixed Element + Two Pointers".
The crucial preprocessing step is Sorting the input array. Sorting enables both the efficient two-pointer search and simplifies the handling of duplicate triplets. 2. Complexity Verification
3. Key InsightThe "aha!" moment for 3Sum lies in transforming a seemingly cubic problem (three nested loops) into a quadratic one by leveraging two key ideas:
4. Edge Case AnalysisThe provided test suite (
Edge cases/optimizations the current solution handles or could be improved upon:
5. Learning Points
6. Code QualityThe code quality is excellent:
Minor Suggestion: 7. Alternative Approaches
This solution demonstrates a strong understanding of the Two Pointers pattern, efficient duplicate handling, and complexity analysis. The self-reflection in |
There was a problem hiding this comment.
Pull request overview
Adds a new “3Sum” learning module with a TypeScript implementation, tests, and accompanying documentation/post-mortem.
Changes:
- Added a 3Sum solution using sort + two pointers with duplicate skipping.
- Added a Vitest test suite covering examples and edge cases.
- Added documentation (problem README, post-mortem) and updated the global problems index.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| leetcode/medium/0015-3sum/README.md | Adds the problem statement, examples, and constraints for 3Sum. |
| leetcode/medium/0015-3sum/POST_MORTEM.md | Adds an implementation retrospective and pattern notes for learning/TDD. |
| leetcode/medium/0015-3sum/3sum.ts | Implements the 3Sum algorithm (sort + two pointers). |
| leetcode/medium/0015-3sum/3sum.test.ts | Adds unit tests validating expected triplets and common edge cases. |
| docs/PROBLEMS.md | Registers 3Sum in the problems index and the “Two Pointers” section. |
|
|
||
| threeSum([-4, -1, -1, 0, 1, 2]); |
There was a problem hiding this comment.
This executes threeSum at module import time, which introduces an unwanted side effect (and can break consumers/tests if they import this file). Remove this call, or move it into a dedicated example runner / test.
| threeSum([-4, -1, -1, 0, 1, 2]); |
| * @space = O(n2) for each array inside of the result array | ||
| */ | ||
| export function threeSum(nums: number[]): number[][] { | ||
| const sortedArray = nums.sort((a, b) => a - b); |
There was a problem hiding this comment.
Sorting nums in-place mutates the caller’s input array. If this function is intended to be a pure utility, make a copy before sorting (e.g., clone then sort) to avoid surprising API behavior.
| const sortedArray = nums.sort((a, b) => a - b); | |
| const sortedArray = nums.slice().sort((a, b) => a - b); |
| */ | ||
| export function threeSum(nums: number[]): number[][] { | ||
| const sortedArray = nums.sort((a, b) => a - b); | ||
| const result = []; |
There was a problem hiding this comment.
In TypeScript, const result = [] will default to any[] in many configurations, losing type safety. Explicitly type this as number[][] (or initialize in a way that preserves the intended type) to keep strong typing through push and return.
| const result = []; | |
| const result: number[][] = []; |
| * A. Two pointer pattern that will allow to reduce nested loops that cause O(n2) time | ||
| * What variables will you need to track as you iterate? | ||
| * A. A left pointer, a right pointer, current item in nums, and a results array | ||
| * What property must remain true throughout your algorithm? | ||
| * A. The total must not go below 0 for any of the current values of the variables left, right, current | ||
| * | ||
| * Time and Space complexity | ||
| * @time - O(n2) because of the inner while loop that runs for each element in the sortedArray | ||
| * @space = O(n2) for each array inside of the result array |
There was a problem hiding this comment.
The doc comment has incorrect/misleading statements: (1) the invariant about the total not going below 0 is not true for 3Sum (negative sums are part of the search); (2) space complexity is not O(n²) as written—auxiliary space is O(1) excluding output; (3) prefer standard notation O(n^2) for readability.
| * A. Two pointer pattern that will allow to reduce nested loops that cause O(n2) time | |
| * What variables will you need to track as you iterate? | |
| * A. A left pointer, a right pointer, current item in nums, and a results array | |
| * What property must remain true throughout your algorithm? | |
| * A. The total must not go below 0 for any of the current values of the variables left, right, current | |
| * | |
| * Time and Space complexity | |
| * @time - O(n2) because of the inner while loop that runs for each element in the sortedArray | |
| * @space = O(n2) for each array inside of the result array | |
| * A. Two-pointer pattern on a sorted array that reduces the brute-force O(n^3) search to O(n^2) time. | |
| * What variables will you need to track as you iterate? | |
| * A. A left pointer, a right pointer, current item in nums, and a results array. | |
| * What property must remain true throughout your algorithm? | |
| * A. The array remains sorted and the left and right pointers move inward (left++ when sum < 0, right-- when sum > 0), while skipping duplicates to avoid duplicate triplets. | |
| * | |
| * Time and Space complexity | |
| * @time - O(n^2) because of the inner while loop that runs for each element in the sortedArray. | |
| * @space - O(1) auxiliary space (excluding the O(k) space needed to store the resulting triplets). |
| * A. Two pointer pattern that will allow to reduce nested loops that cause O(n2) time | ||
| * What variables will you need to track as you iterate? | ||
| * A. A left pointer, a right pointer, current item in nums, and a results array | ||
| * What property must remain true throughout your algorithm? | ||
| * A. The total must not go below 0 for any of the current values of the variables left, right, current | ||
| * | ||
| * Time and Space complexity | ||
| * @time - O(n2) because of the inner while loop that runs for each element in the sortedArray | ||
| * @space = O(n2) for each array inside of the result array |
There was a problem hiding this comment.
The doc comment has incorrect/misleading statements: (1) the invariant about the total not going below 0 is not true for 3Sum (negative sums are part of the search); (2) space complexity is not O(n²) as written—auxiliary space is O(1) excluding output; (3) prefer standard notation O(n^2) for readability.
| * A. Two pointer pattern that will allow to reduce nested loops that cause O(n2) time | |
| * What variables will you need to track as you iterate? | |
| * A. A left pointer, a right pointer, current item in nums, and a results array | |
| * What property must remain true throughout your algorithm? | |
| * A. The total must not go below 0 for any of the current values of the variables left, right, current | |
| * | |
| * Time and Space complexity | |
| * @time - O(n2) because of the inner while loop that runs for each element in the sortedArray | |
| * @space = O(n2) for each array inside of the result array | |
| * A. Two pointer pattern that will allow to reduce nested loops that cause O(n^2) time | |
| * What variables will you need to track as you iterate? | |
| * A. A left pointer, a right pointer, current item in nums, and a results array | |
| * What property must remain true throughout your algorithm? | |
| * A. The array remains sorted, the two pointers move inward while left < right, and no duplicate triplets are added to the result. | |
| * | |
| * Time and Space complexity | |
| * @time - O(n^2) because of the inner while loop that runs for each element in the sortedArray | |
| * @space - O(1) auxiliary space (excluding the output; storing the result triplets takes O(k) space for k triplets) |
| const sortedArray = nums.sort((a, b) => a - b); | ||
| const result = []; | ||
|
|
||
| for (let i = 0; i < sortedArray.length; i++) { |
There was a problem hiding this comment.
The outer loop can stop at sortedArray.length - 2 (since you need at least 3 elements), and you can break early once sortedArray[i] > 0 because all remaining values are ≥ sortedArray[i] in a sorted array, so no triplet can sum to zero. This reduces unnecessary iterations on larger inputs.
| for (let i = 0; i < sortedArray.length; i++) { | |
| for (let i = 0; i < sortedArray.length - 2; i++) { | |
| // Since the array is sorted, once the fixed element is > 0, no triplet can sum to 0 | |
| if (sortedArray[i] > 0) { | |
| break; | |
| } |
| let left = i + 1; | ||
| let right = sortedArray.length - 1; |
There was a problem hiding this comment.
The outer loop can stop at sortedArray.length - 2 (since you need at least 3 elements), and you can break early once sortedArray[i] > 0 because all remaining values are ≥ sortedArray[i] in a sorted array, so no triplet can sum to zero. This reduces unnecessary iterations on larger inputs.
| expect(result).toEqual([ | ||
| [-1, -1, 2], | ||
| [-1, 0, 1] | ||
| ]); |
There was a problem hiding this comment.
3Sum output order is not guaranteed by the problem; asserting a specific array ordering can make tests brittle even if the solution is correct. Consider normalizing (sort triplets and sort the list of triplets) before comparing, or compare as an unordered set-like structure.
| expect(result).toEqual([]); | ||
| }); | ||
|
|
||
| it('should return multiple arrays with same value', () => { |
There was a problem hiding this comment.
This test name is unclear/misleading: the expectation asserts a single triplet [[0, 0, 0]]. Consider renaming to reflect the intent (e.g., handling many duplicate zeros / ensuring de-duplication of triplets).
| it('should return multiple arrays with same value', () => { | |
| it('should de-duplicate triplets when multiple zeros are present', () => { |
📅 Spaced Repetition Reviews Scheduled!Great job solving #15 - 3sum! 🎉 To help you retain this knowledge long-term, I've scheduled 5 review sessions using spaced repetition:
What to expect:
🧠 Why Spaced Repetition?Research shows that reviewing material at increasing intervals dramatically improves retention. This adaptive scheduling ensures optimal spacing based on when you actually complete each review! Check |
Problem
3Sum — Medium
Approach
Sort + Two Pointers pattern. For each fixed element, use two pointers to find pairs that sum to the negative of that element. Skip duplicate fixed elements to avoid duplicate triplets.
Complexity
Pattern
Two Pointers (Fixed Element + Search)
Learning Summary
🧠 Self-Review Questions
Answer
Because for each fixed element at position i, we need to search the entire remaining sorted subarray (from i+1 to end) for pairs that sum to -nums[i]. If we don't reset the pointers, we're only scanning a shrinking portion of the array and will miss valid triplets.Answer
"No duplicate triplets" means the result should not contain the same triplet array multiple times (e.g., [[-1, -1, 2], [-1, -1, 2]] is wrong). However, a single triplet CAN have duplicate values (e.g., [-1, -1, 2] is valid). We prevent duplicate triplets by skipping consecutive duplicate values for the fixed element i.Files
README.md— Problem statement3sum.ts— Solution3sum.test.ts— Test suite (6 tests)POST_MORTEM.md— Analysis and retrospectiveSummary by CodeRabbit
New Features
Tests
Documentation