这道题是双指针问题的一个变式问题,但是一开始处理起来会显得比较棘手。因为它同时涉及到了三个指针,三个指针的搜索范围有重叠,但是在最终输出结果里要求不能有重复的三元组。
题目详情如下:
解决这个问题的时候可以使用转化的思想,将三个指针中的一个暂时固定(fixed),剩下两个指针像传统的双指针那样工作。双指针分为对撞指针和快慢指针。这样的题目显然是在寻找正负数相抵消的情况,是对撞指针擅长处理的问题。対撞指针还需要遍历的序列有序,所以可以想见需要事先对输入数组进行排序。
为了不让结果含有重复的三元组,我们需要在遍历整个数组的同时“去重”。排序之后的数组是有序的,相同的元素都相邻的存放在一起。去重需要做的就是将相同的相邻元素只处理一次,剩下的全部跳过,这样不仅提升了搜索效率,也同时实现了去重。我在AC代码中写了以下函数来跳过相邻重复元素:
// return the next element index, jump through all the same elements in between
// Direction = 0, ->
// Direction = 1, <-
void ModifyIndex(int& Present, int Terminal, vector<int>& nums, bool Direction)
{
if(!Direction)
while(Present < Terminal && nums[Present] == nums[Present - 1])
++Present;
else
while(Present > Terminal && nums[Present] == nums[Present + 1])
--Present;
}
有了这个函数,就可以开始编写核心代码了,如同上面所说,让一个指针固定(Fixed),剩下两个指针在[Fixed+1, nums.size() - 1]的范围内进行传统的对撞指针运算。当Fixed指针从[0, nums.size()-2]遍历完整一遍时,我们就得到了全部解,当然这里指针的修改全部要使用ModifyIndex来跳过重复的元素。
vector<vector<int>> threeSum(vector<int>& nums)
{
vector<vector<int>> Result;
// if the size of input vector less than 3, return null vector
if(nums.size() < 3)
return Result;
// sort the input vector as ascending order
sort(nums.begin(), nums.end());
// keep a pointer fixed, let the other 2 pointers search the solution space
int Fixed = 0;
while(Fixed < nums.size() - 1) // outer loop [0, nums.size() - 2]
{
int Left = Fixed + 1, Right = nums.size() - 1;
while(Left < Right) // inner loop [Fixed + 1, nums.size() - 1]
{
// if the sum is less than 0
if(nums[Left] + nums[Right] + nums[Fixed] < 0)
ModifyIndex(++Left, Right, nums, 0);
else if(nums[Left] + nums[Right] + nums[Fixed] > 0)
ModifyIndex(--Right, Left, nums, 1);
else
{
Result.push_back({
nums[Left], nums[Right], nums[Fixed]});
ModifyIndex(++Left, Right, nums, 0);
ModifyIndex(--Right, Left, nums, 1);
}
}
// modify the fixed pointer position
ModifyIndex(++Fixed, nums.size() - 1, nums, 0);
}
return Result;
}
上述代码就完成了这样一个双指针的变式问题。