
Using JavaScript and the Two‑Pointer Technique to Solve 4Sum

Following on from my recent articles solving the 2sum and 3sum problems with JavaScript, it makes perfect sense that we now delve into the next problem in the sequence: the 4Sum problem.
As with other LeetCode‑type problems, these are classic interview questions: challenges that require some creative thinking alongside a solid understanding of array manipulation and classic algorithmic strategy.
As with the sibling 2/3Sum problems, the solution lies in implementing the two‑pointer technique, a strategy which can efficiently tackle various array‑related problems.
Understanding the 4Sum Problem
The 4Sum problem is a variation on the well‑known 2Sum and 3Sum (or Two Sum and Three Sum) problems.
In this instance, we're given an array of integers and a target value and then asked to find all unique quadruplets in the array that sum up to the given target. It's important to note here that I've often seen two variations of this (and the 2/3Sum counterparts) where the expectation is that the returns sum up to zero, rather than requiring an additional target input. For 4Sum though, let's accept a target too.
An Example
For a more clear example, let's say that we have the array: [1, 0, ‑1, 0, ‑2, 2] and the target sum 0.
We would expect our function to produce the quadruplets [‑2, ‑1, 1, 2] and [‑2, 0, 0, 2] ‑ in both quadruplets, the total sum is the target: zero.
The Two‑Pointer Technique
Our weapon of choice to solve the 4Sum problem ‑ much like before ‑ is the two‑pointer technique. This technique involves sorting the array and then using two pointers to navigate through the array from one end to the other in a way that reduces the search space whilst trying to find the desired sum.
How the Two‑Pointer Technique Works in Practice
- Sort the array in ascending order.
- Iterate through the array with two pointers: one starting from the left (
low), and the other starting from the right (high). - Calculate the sum of the elements at the current
lowandhighpositions, as well as the other two pointers in between. - If the sum is equal to the
target, we've found a valid quadruplet. - If it's less than the target, we move the
lowpointer to the right. - If it's greater, we move the
highpointer to the left. - Continue iterating and adjusting the pointers until the
lowpointer is less than thehighpointer.
Implementing a Solution Using JavaScript (and TypeScript)
Given my field, it will come as no surprise that I'm going to offer a solution using front‑end technologies. Here's a rough solution:
const fourSum = (nums: number[], target: number): number[][] => { const result: number[][] = []; const n: number = nums.length; nums.sort((a, b) => a - b); for (let i = 0; i < n - 3; i++) { if (i > 0 && nums[i] === nums[i - 1]) continue; for (let j = i + 1; j < n - 2; j++) { if (j > i + 1 && nums[j] === nums[j - 1]) continue; let low = j + 1; let high = n - 1; while (low < high) { const sum = nums[i] + nums[j] + nums[low] + nums[high]; if (sum === target) { result.push([nums[i], nums[j], nums[low], nums[high]]); while (low < high && nums[low] === nums[low + 1]) low++; while (low < high && nums[high] === nums[high - 1]) high--; low++; high--; } else if (sum < target) { low++; } else { high--; } } } } return result;};By sorting the array and using the two‑pointer technique, this efficiently explores all possible combinations whilst attempting to avoid duplicates. The algorithm's time complexity is O(n^3) due to the nested loops, and the space complexity is O(1) since we're using a constant amount of extra space.
Adding Some Tests
As I've covered previously, it is always worth offering some test coverage when writing functionality like this, not least because it might make your application stand out against the others!
In the same vein as I wrote for the 3Sum problem, here are some quick Cypress tests:
describe('4Sum', () => { it('should find all unique quadruplets that sum up to zero', () => { expect(fourSum([-1, 0, 1, 2, -1, -4], 0)).toEqual([ [-4, -1, 1, 4], [-4, 0, 0, 4], [-4, 0, 1, 3], [-4, -1, 0, 5], [-1, -1, 0, 2], [-1, -1, 1, 1], ]); expect(fourSum([0, 0, 0, 0], 0)).toEqual([[0, 0, 0, 0]]); expect(fourSum([-2, 0, 0, 2, 2], 0)).toEqual([ [-2, 0, 2, 0], [-2, 0, 0, 2], ]); }); it('should find all unique quadruplets that sum up to the target', () => { expect(fourSum([-1, 0, 1, 2, -1, -4], 1)).toEqual([ [-4, 0, 1, 4], [-1, -1, 1, 2], ]); expect(fourSum([0, 0, 0, 0], 0)).toEqual([[0, 0, 0, 0]]); expect(fourSum([-2, 0, 0, 2, 2], 0)).toEqual([ [-2, 0, 2, 0], [-2, 0, 0, 2], ]); }); it('should handle empty and small arrays', () => { expect(fourSum([], 10)).toEqual([]); expect(fourSum([1], 10)).toEqual([]); expect(fourSum([1, 2], 10)).toEqual([]); });});Wrapping‑Up
So there you have it! We've explored the 4Sum problem, dived into the two‑pointer technique, and implemented a solution in JavaScript using ES6 and TypeScript. Remember, understanding classic algorithmic problems and efficient solutions can greatly enhance your problem‑solving skills. Plus, testing your code using tools like Cypress ensures its reliability.
Categories:
Related Articles

The Power of text‑wrap: pretty. The Power of

Solving the LeetCode Two Sum Problem Using JavaScript. Solving the LeetCode Two Sum Problem Using JavaScript

3Sum Closest in JavaScript: Sorting and Two Pointers. 3Sum Closest in JavaScript: Sorting and Two Pointers

JavaScript Symbols: When and Why to Use Them. JavaScript Symbols: When and Why to Use Them

Responsive JavaScript and the matchMedia Method. Responsive JavaScript and the
matchMediaMethod
LeetCode Container with Most Water: The Two‑Pointer Solution. LeetCode Container with Most Water: The Two‑Pointer Solution

Lazy Loading in Angular: Optimising Performance. Lazy Loading in Angular: Optimising Performance

Building a Headless CMS‑Powered Site with Next.js. Building a Headless CMS‑Powered Site with Next.js

Understanding prototype.apply() in JavaScript. Understanding
prototype.apply()in JavaScript
Check If Three Values are Equal in JavaScript. Check If Three Values are Equal in JavaScript

Renaming and Destructuring Variables in ES6. Renaming and Destructuring Variables in ES6

Understanding WeakMap and WeakSet in JavaScript. Understanding
WeakMapandWeakSetin JavaScript