トピックの説明:
問題解決のアイデア: ダブルポインタ
まず、配列を昇順に並べ替えます。次に、次の手順を使用して問題を解決できます。
空の結果セット result を初期化して、合計が 0 である見つかったトリプルを格納します。
最後から 3 番目の要素まで配列全体をスキャンします (必要なのはトリプルであるため)。各要素について、2 つのポインター (左ポインター L と右ポインター R) を使用して、合計が 0 であるすべてのトリプルを検索します。
- 左ポインタ L を初期化して現在の要素の次の要素に設定し、右ポインタ R を初期化して配列の最後の要素に設定します。
- 現在の要素が前の要素と同じである場合 (存在する場合)、重複したソリューションを避ける必要があるため、単純に現在の要素をスキップし、次の要素に進みます。
- 現在の要素 nums[i] について、nums[i] + nums[L] + nums[R] の合計を確認します。
- 合計が 0 未満の場合は、左ポインタ L をインクリメントします。
- 合計が 0 より大きい場合は、右ポインタ R を減らします。
- 合計が 0 に等しい場合は、このトリプレット [nums[i], nums[L], nums[R]] を結果セットの結果に追加し、合計が 0 になるまで左ポインタ L と右ポインタ R を同時に移動します。現在の要素と同じ要素が見つかりました 等しくない要素 (ソリューションの重複を避けるため)。
- 最後に、結果セットの結果が返されます。
配列全体を走査する必要があり、各要素を 1 回走査する必要がある場合があるため、このメソッドの時間計算量は O(n^2) です。
実装を作成するときは、解決策の重複を避けるために、左右のポインターを処理するときに必ず等しくない要素に移動してください。
コード:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size() - 1; i++)
{
if(nums[i] > 0)
return result; //数组递增
if(i > 0 && nums[i] == nums[i - 1])
{
continue; //对a去重,如果i=1与i=0对应的元素相同,那么跳过下面的所有操作,left,right不需要考虑了。
}
//a满足去重,再来确定left,right以及去除问题。
int left = i + 1;
int right = nums.size() - 1;
while(left < right)
{
//当left==right时,b,c为同一个数,不符合要求。
if(nums[i] + nums[left] + nums[right] < 0)
left++;
else if(nums[i] + nums[left] + nums[right] > 0)
right--;
else
{
vector<int> v = {nums[i], nums[left], nums[right]};
result.push_back(v);
//下面考虑left,right的移动情况。
while (right > left && nums[right] == nums[right - 1])
right--;
//再次加上right>left的原因是:进入上面的循环时是复合right>left的,但是
//可能在循环中left与right发生改变。
while (right > left && nums[left] == nums[left + 1])
left++;
/*
为什么是while?举个例子
[0 -1 -1 -1 -1 -1 1 1 1 1 1 1 ]
因为当i= 0时,left与right就已经收集到了正确的结果
此时应该持续地移动left 与 right,一次是不够。
*/
left++;
right--;
//是为了在找到一个有效的三元组后,将left和right分别向前和向后移动一位,以便在后续的循环中继续寻找其他可能的三元组。这样可以确保所有的有效三元组都被找到。
}
}
}
return result;
}
};