アルゴリズムを学び、フォースボタンを磨き、ロールに燃料を補給し、大きな工場に入ります!
トピックの説明
n個の整数を含む配列numsが与えられた場合、a + b + c = 0となるようなnumsに3つの要素a、b、cがあるかどうかを判断しますか?合計が0で、繰り返されないすべてのトリプルを見つけてください。
注:回答に重複するトリプルを含めることはできません。
例1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
例2:
输入:nums = []
输出:[]
例3:
输入:nums = [0]
输出:[]
ヒント:
- 0 <= nums.length <= 3000
- -105 <= nums [i] <= 105
知識ポイントを含む
これはやや難しい質問ですが、考え方は比較的単純です。最初に頭に浮かぶのはブルートフォースソリューションですが、3つのforループのブルートフォースソリューションがタイムアウトであり、その時間計算量がO(n³)であることは明らかです。しかし、考え方は正しいです。これを最適化して、時間の複雑さを軽減することができます。最初の方法は、ハッシュテーブル方式を使用することです。つまり、最初に並べ替え、次に2つのforループを使用して2つの要素の合計を見つけ、残りの要素にハッシュテーブルを使用して、この値があるかどうかを判断します(注2番目の方法はダブルポインター方式です。つまり、最初に配列がソートされ、次にダブルポインターの移動を使用して、条件を満たす値が検索されます。
上記の分析に基づいて、私たちが知る必要のある知識ポイントは次のとおりです。
- 配列は、連続したメモリ空間に格納されている同じタイプのデータのコレクションです。
- 配列の添え字は0から始まります
- メモリ空間内のアレイのアドレスは連続しています
- 配列は単純なハッシュテーブルであり、ハッシュテーブルのキーは配列のインデックス添え字です。
- ハッシュテーブルは、キーに基づいてデータに直接アクセスできます
- HashSetのcontainsは、ハッシュテーブルにこの値があるかどうかを判断できます
次に、タイトルに従って、キーポイントを抽出できます。
- 合計が0で、繰り返されないトリプル
質疑応答
Java問題解決策1
思考分析
まず、ハッシュテーブル方式を使用します。つまり、最初に並べ替え、次に2つのforループを使用して2つの要素の合計を見つけ、残りの要素にハッシュテーブルを使用して、この値があるかどうかを判断します(注意してください重複排除)
- 配列要素を並べ替える
- 次に、double forループを使用して、重複するタプルを削除します
- 条件を満たすセットに参加する
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//处理特殊情况
if(nums.length<3)
return new ArrayList<List<Integer>>();//实例化只需要最外层指定具体实现集合类
List<List<Integer>> res = new ArrayList<List<Integer>>();//初始化结果集合
Arrays.sort(nums); //排序
for(int i=0;i<nums.length;i++){
if(i>0 && nums[i]==nums[i-1])
continue; //去掉重复元组
int target = -nums[i];
HashSet<Integer> set = new HashSet<Integer>(); //集合来判断是否满足条件
for(int j=i+1;j<nums.length;) {
//如果存在元素满足条件,执行以下逻辑
if(set.contains(target-nums[j])){
ArrayList<Integer> tmp = new ArrayList<Integer>();
tmp.add(nums[i]);
tmp.add(nums[j]);
tmp.add(target-nums[j]);
res.add(tmp);
//如果寻找到解,那么不需要将nums[j]加入set,因为此时已经将nums[j]作为答案赋值给res了,nums[j]已经不可能和其他元素搭配组成答案了。
//寻找到一个解,如果下一个元素值和当前值一样,那么会出现一个重复的答案,所以直接跳过,直到出现不一样的元素
j++;
while(j<nums.length&&nums[j]==nums[j-1]){
j++;
}
}else{
//如果当前元素不存在,同时也不满足解条件,那么将当前元素加入set
set.add(nums[j++]);
}
}
}
return res;
}
}
Java問題ソリューション2
思考分析
2番目の方法は、ダブルポインター方式です。つまり、最初に配列がソートされ、次にダブルポインターの移動を使用して、条件を満たす値が検索されます。具体的なアイデアは次のとおりです。
- 重複排除を簡単にするために最初に並べ替える
- 次に、トラバーサルを実装するための最初の固定ポインターkを定義します
- 次に、ダブルポインタの左と右のポインタiとjがあり、左のポインタiはkの後ろにあり、右のポインタjは配列の右端にあります。
- 左右のポインタが交差しない場合は、左のポインタが右に移動し、右のポインタが左に移動します。
- 条件が満たされている場合はリストコレクションに追加します。満たされていない場合は、固定ポインターを移動した後、上記の手順を繰り返します。
コード
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums); //数组排序,方便去重
List<List<Integer>> res = new ArrayList<>(); //结果集合
for(int k = 0; k < nums.length - 2; k++){
//k是第一个固定的指针
if(nums[k] > 0) //k位置的都大于0了,后面的肯定都大于0了
break;
if(k > 0 && nums[k] == nums[k - 1]) //去重
continue;
int i = k + 1; //双指针的左边的指针
int j = nums.length - 1; //双指针的右边的指针
while(i < j){
//满足左边指针在左边,右边指针在右边(不交叉)
int sum = nums[k] + nums[i] + nums[j]; //求和
if(sum < 0){
//和小于0,左边指针向右移动
while(i < j && nums[i] == nums[++i]);
} else if (sum > 0) {
//和大于00,右边指针向左移动
while(i < j && nums[j] == nums[--j]);
} else {
//和等于0,满足条件,元素放入结果集合
res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
//想要找下一个0的话,必须左指针元素值增加,右指针元素值减小
while(i < j && nums[i] == nums[++i]);//左指针继续右移(在0的基础上增加)
while(i < j && nums[j] == nums[--j]); //右指针继续左移(在0的基础上减少)
}
}
}
return res; //返回结果集合
}
}