3Sum: an N^2 solution

https://leetcode.com/problems/3sum/, or copied/pasted here:
Given an array S of n integers, are there elements abc in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
  [-1, 0, 1],
  [-1, -1, 2]
]
A naive solution would take N^3-time (3 nested loops). Here is a faster one:

  • Start by sorting the input array (NLogN)
  • Transform the equation into a+b=-c
  • The outer loop will be the "-c", which will go from 0 to nums.length - 1
  • For the inner loop, use the 2-pointers technique: one pointing to the leftmost index, one pointing to the rightmost index, and move them accordingly depending on the result of the addition in respect to "-c". The inner loop will also be O(N).
  • There is a need for a hash table to keep track of the triplets already added to the solution
  • Total execution time: NLogN + N^2, hence O(N^2)-time

Barely made the list (beating just 9% of the other C# submissions). Code is down below, cheers, Marcelo.


    public class Solution
    {
        public IList<IList<int>> ThreeSum(int[] nums)
        {
            Array.Sort(nums);

            List<IList<int>> solution = new List<IList<int>>();
            Hashtable htSolution = new Hashtable();
            for (int i = 0; i < nums.Length; i++)
            {
                int c = -nums[i];
                int head = i + 1;
                int tail = nums.Length - 1;

                while(head < tail)
                {
                    int a = nums[head];
                    int b = nums[tail];

                    if (a + b == c)
                    {
                        string key = (-c).ToString() + "@" + a.ToString() + "@" + b.ToString();
                        if (!htSolution.ContainsKey(key))
                        {
                            htSolution.Add(key, true);
                            List<int> list = new List<int>();
                            list.Add(-c);
                            list.Add(a);
                            list.Add(b);
                            solution.Add(list);
                        }
                        head++;
                        tail--;
                    }
                    else if (a + b > c)
                    {
                        tail--;
                    }
                    else
                    {
                        head++;
                    }
                }
            }

            return solution;
        }
    }

Comments

  1. Awesome problem! Since input array is sorted, it's possible to avoid duplicates without having to resort to hashset - we can simply skip all repeated characters by advancing index of the outer loop, as well as head and tail indices:

    class Solution {
    public:
    vector> threeSum(vector& nums) {
    if (nums.size() < 3) return {};
    sort(nums.begin(), nums.end());
    vector> result;
    for (int i = 0; i < nums.size()-2; ++i) {
    int target = -nums[i];
    int left = i + 1;
    int right = nums.size() - 1;
    while (left < right) {
    int sum = nums[left] + nums[right];
    if (sum < target) {
    left += 1;
    } else if (sum > target) {
    right -= 1;
    } else {
    result.push_back({nums[i], nums[left], nums[right]});
    while (left + 1 < right && nums[left] == nums[left+1]) left += 1;
    while (right - 1 > left && nums[right] == nums[right-1]) right -= 1;
    left += 1;
    right -= 1;
    }
    while (i + 1 < nums.size() && nums[i] == nums[i+1]) i += 1;
    }
    }
    return result;
    }
    };

    ReplyDelete

Post a Comment

Popular posts from this blog

Golang vs. C#: performance characteristics (simple case study)

Claude vs ChatGPT: A Coder's Perspective on LLM Performance

ProjectEuler Problem 719 (some hints, but no spoilers)