Linked List · 21 of 36
Find pairs with a given sum in a DLL
In a sorted chain where every box can step forward and back, find every pair of boxes whose numbers add up to a target.
- Simple way
- time O(n^2)
room O(1) - Clever way
- time O(n)
room O(1)
The problem, simply
You get a doubly linked list: every box has an arrow to the next box and an arrow back to the box before it. The numbers are sorted from small to big, and no number repeats. You also get a target.
Find every pair of boxes whose numbers add up to target. Give back the pairs, smaller number first, in increasing order of the first number.
A story to remember it
Picture it
A sorted doubly linked list. Each box has an arrow forward and an arrow back, so we can walk from either end. With target 7, the pairs are (1, 6) and (2, 5).
- answer
Examples
A normal case
- In
1 2 4 5 6 8 9, target = 7- Out
(1, 6), (2, 5)
Two pairs balance. 4 cannot pair with itself because each box is used once in a pair.
One pair
- In
1 5 6, target = 6- Out
(1, 5)
1 + 5 = 6. The 6 has no partner (0 is not there).
No pair
- In
1 2 3, target = 10- Out
none
The biggest possible sum is 2 + 3 = 5. Return an empty list.
Too short
- In
4, target = 8- Out
none
One box cannot make a pair, even with itself.
Way 1: the simple way
Try every pair.
- Put finger
aon each box in turn. - For each
a, walk fingerbover every box after it. - If
a.data + b.data == target, write the pair down. Since the list is sorted, you can stop the inner walk as soon as the sum gets too big.
With a on 1, finger b tries 2, 4, 5 and then 6, which works. Then a moves to 2 and b starts over.
- looking now
- seen
- answer
class Solution {
public static List<List<Integer>> findPairsWithGivenSum(int target, Node head) {
List<List<Integer>> out = new ArrayList<>();
for (Node a = head; a != null; a = a.next) {
for (Node b = a.next; b != null; b = b.next) {
int sum = a.data + b.data;
if (sum == target) out.add(Arrays.asList(a.data, b.data));
if (sum >= target) break; // sorted, so later b only get bigger
}
}
return out;
}
}How long it takes: about n * n / 2 pairs in the worst case, O(n^2). Extra room: two fingers, O(1), not counting the answer.
Way 2: the clever way
Use the seesaw trick with two pointers. The back arrows are what make it possible: right can walk from the tail toward the head.
- Put
lefton the head. Walk to the end once to putrighton the tail. - While
leftis beforeright: add the two numbers. - Equal to the target? Save the pair, move
leftforward andrightback. - Too big? Move
rightback. Too small? Moveleftforward. - Stop when the fingers meet or cross.
Squeeze from both ends
01 / 06
left starts on the smallest box, right on the biggest. The list is sorted and each box can step back, so right can walk left.
- target
- 7
- looking now
- checking
- answer
14
class Solution {
public static List<List<Integer>> findPairsWithGivenSum(int target, Node head) {
List<List<Integer>> out = new ArrayList<>();
if (head == null) return out;
Node left = head, right = head;
while (right.next != null) right = right.next; // walk to the tail once
while (left != right && right.next != left) { // stop when they meet or cross
int sum = left.data + right.data;
if (sum == target) {
out.add(Arrays.asList(left.data, right.data));
left = left.next;
right = right.prev;
} else if (sum > target) {
right = right.prev; // too heavy, lighter partner
} else {
left = left.next; // too light, heavier partner
}
}
return out;
}
}How long it takes: one walk to the tail, then every step moves at least one finger inward, O(n). Extra room: two fingers, O(1).
Watch out for
- Forgetting to find the tail first.
rightmust start at the last box, not atnull. - Using the trick on an unsorted list. It only works because sorted order tells you which finger to move.
- Duplicates. If values can repeat, a match like (2, 5) with two 2s and three 5s is really 6 pairs. Count the runs of equal values on both sides and multiply.
- A singly linked list. There is no back arrow, so either copy into an array or use a hash set of seen values in one pass.
What interviewers ask next
- Do it on a singly linked list. One pass with a hash set: for each value, check if
target - valueis in the set, then add the value.O(n)time andO(n)space. - Count triplets that add up to
x. Fix the first box, then run this same two pointer squeeze on the boxes after it. That isO(n^2). - Find the pair whose sum is closest to
target. Same squeeze, but keep the best|sum - target|seen so far instead of only exact matches. - What if the list is a binary search tree instead? Walk it in order from the small end and the big end at the same time with two stacks, the same seesaw idea.
Remember this
Grown-up words: the kids lined up by weight are a sorted doubly linked list, the back arrows are prev pointers, the two hands are the two pointer technique, and sending kids away is shrinking the search window.