Skip to content
dsa450for five year olds

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

doubly linked list
null1245689tailhead

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.

  1. Put finger a on each box in turn.
  2. For each a, walk finger b over every box after it.
  3. 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.
doubly linked list
null1245689ba

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
java
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.

  1. Put left on the head. Walk to the end once to put right on the tail.
  2. While left is before right: add the two numbers.
  3. Equal to the target? Save the pair, move left forward and right back.
  4. Too big? Move right back. Too small? Move left forward.
  5. Stop when the fingers meet or cross.

Squeeze from both ends

01 / 06

doubly linked list
null1245689rightleft

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

java
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. right must start at the last box, not at null.
  • 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 - value is in the set, then add the value. O(n) time and O(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 is O(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.