Proofs: 3SUM by sorting and two-pointer scan
These are the human proofs of 3SUM by sorting and two-pointer scan, at full length.
The sort-and-scan 3SUM algorithm reports each solution exactly once full
This proof shows: correctness, termination.
We use the pseudocode of the page. After line L1, is the sorted input. A solution is a value triple with , , such that there are positions of with . Since holds the same multiset of values as the input, this is the same set of solutions as for the input list.
Claim. THREE-SUM terminates, and it reports each solution exactly once and nothing else.
Step 1: one sweep
Fix an that line L3 does not skip, and let and . Call a value pair good if , , and there are positions with and . We show that the sweep (lines L4 to L11) reports each good pair exactly once and nothing else.
Write for the set of good pairs that are not yet reported. We use this invariant at each test of line L5:
(Q) and . (a) Each has positions with , and . (b) Each pair already reported in this sweep is good, and if it has and .
Initialization. Line L4 sets and . Nothing is reported, so is the set of all good pairs, and by definition each of them has positions with . So (a) holds, and (b) holds with nothing to check.
Maintenance. Assume (Q) and . Line L6 sets .
-
Case (line L7). Then . For each with , , so no pair of positions in with first position gives a good pair. So the positions of (a) have , and (a) holds for the new . Part (b) holds because and does not change.
-
Case (line L8). Then . For each with , . So the positions of (a) have , and (a) holds for the new . Part (b) holds because .
-
Case (lines L9 to L11). Let and . Then , , and the positions show that is good. By (b), each pair reported before has , so was not reported before: line L9 reports it for the first time, and it leaves .
Now take any after this report, with positions from (a). Then . If then , which is not possible, so . If , then with , which contradicts the sorted order. So , and then .
Line L10 sets to and then moves it right while and . Every position that it passes over holds the value . Since , position is not passed over, unless the loop stops early because ; we treat that below. Line L11 does the same for with the value , and position , with , is not passed over. So if the loops stop because the next value differs, the new range still contains and , and (a) holds.
If a loop stops because , then all positions from the old to the current one hold (or all positions from the current to the old one hold ). Every remaining pair of the old range then has or ; by the paragraph above, no pair of has this, so is empty and (a) holds trivially.
For (b): after line L10, either , or and so , which is at least as large as each reported so far. After line L11, in the same way, or . Each earlier reported pair had $z > $ the old , so all reported pairs have .
Termination of the sweep. Each iteration decreases by at least 1: lines L7 and L8 by exactly 1, lines L10 and L11 by at least 2. The value starts at , and the loop runs only while it is at least 1. So the sweep stops after at most iterations.
End of the sweep. When the loop stops, , so no pair of positions exists, and by (a) the set is empty: every good pair has been reported. By (b), and since each pair is reported only in the case where it was not reported before, no good pair is reported twice, and every reported pair is good.
Step 2: the outer loop
Let be a solution, with positions . Let be the smallest position with . Then , so is a good pair for . Position is not skipped by line L3, because or . Also , since and . By Step 1, the sweep for reports .
The sweeps that line L3 does not skip are exactly the first positions of the distinct values of , so no two of them have the same . Each sweep reports its good pairs once, with its own . So each solution is reported exactly once, and each reported triple is a solution by Step 1.
The outer loop runs times and each sweep terminates, so the algorithm terminates.
Cost. By the termination argument, the total number of iterations of line L5 is at most , and each costs besides the inner while loops of lines L10 and L11, whose steps are counted in the decrease of . So the time after the sort is .