日常生活中经常遇到求解数组中是否存在和为0的三个数,即3-sum问题,为此本文介绍一些比较实用的方法,并在暴力计算的基础上对算法逐步改进,以达到最优的算法。
首先介绍一下2-sum问题,3-sum问题其实就是2-sum问题的一个扩展而已。对于2-sum问题,最一般的想法就是使用两层循环直接枚举数组中的所有元素对,直到找到和为0的元素对为止。
int sum_2()
{
int res = 0;
int n = data.size();
for(int i=0; i<n; i++)
{
for(int j=i+1; j<n; j++)
{
if(data[i] + data[j] == 0)
{
res ++;
}
}
}
return res;
}
上述算法的由于包含了两层循环,因此时间复杂度为O(N^2)。
观察发现,上述算法时间主要花费在数据比对,为此可以考虑使用二分查找来减少数据比对时间,要想使用二分查找,首先应该对数据进行排序,在此使用归并排序对数组进行升序排列。排序所花时间为O(NlogN),排序之后数据查找只需要O(logN)的时间,但是总共需要查找N此,为此改进后算法的时间复杂度为O(NlogN)。
int cal_sum_2()
{
int res = 0;
for(int i=0; i<data.size(); i++)
{
int j = binary_search(-data[i]);
if(j > i)
res++;
}
return res;
}
观察上述算法发现,我们在比对的过程中还是存在了一些冗余。因为排列后的数据是从最小的数开始匹配的,我们只需计算其与最后的数据的和是否为0即可,如果大于0,则说明不存在与最小数匹配的数,此时将用较小的数来替代最大的数,反之则选用较大的数替代最小的数,如此反复,只需要扫