3Sum: an N^2 solution
https://leetcode.com/problems/3sum/, or copied/pasted here:
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;
}
}
A naive solution would take N^3-time (3 nested loops). Here is a faster one:Given an array S of n integers, are there elements a, b, c 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] ]
- 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;
}
}
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:
ReplyDeleteclass 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;
}
};