團滅二數之和、三數之和、四數之和
阿新 • • 發佈:2020-10-08
1.兩數之和
題目連結
題目描述
解題思路
1.暴力法
雙重for迴圈,時間複雜度O(n*n)
2.排序+雙指標
3.雜湊表
AC程式碼
//利用雜湊表,一步到位,時間複雜度O(n) class Solution { public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>(); for (int i = 0; i < nums.length; ++i) { if (hashtable.containsKey(target - nums[i])) { return new int[]{hashtable.get(target - nums[i]), i}; } hashtable.put(nums[i], i); } return new int[0]; } }
2.三數之和
題目連結
題目描述
解題思路
1.最樸素的解法:暴力,三重for迴圈,可能會超時
2.排序+雙指標
注意本題不適合採用雜湊表,如果採用雜湊表,需要去重操作,需要注意更多的細節。
首先對陣列進行排序,排序後固定一個數 nums[i],再使用左右指標指向nums[i]後面的兩端,數字分別為 nums[L]和nums[R],計算三個數的和 sum,判斷是否滿足為 0,滿足則新增進結果集
-
如果nums[i]大於0,則三數之和必然無法等於0,結束迴圈
-
如果 nums[i] == nums[i-1],則說明該數字重複,會導致結果重複,所以應該跳過
-
當 sum == 0 時,nums[L] == nums[L+1] 則會導致結果重複,應該跳過,L++
-
當 sum == 0 時,nums[R] == nums[R-1]則會導致結果重複,應該跳過,R−−
時間複雜度:O(n^2)
AC程式碼
//利用set集合去重,剛好卡著時間過,去重是可以通過手寫程式碼優化,不需要利用set集合。 class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> ans = new LinkedList<>(); Set<LinkedList<Integer>> st = new HashSet<>(); Arrays.sort(nums); int index = 0; if(nums.length==0) return ans; while(true){ if(nums[index] > 0 || index > nums.length - 3) break; int l = index+1; int r = nums.length-1; while(l < r){ if(nums[index]+nums[l]+nums[r] == 0){ LinkedList<Integer> temp = new LinkedList<>(); temp.add(nums[index]); temp.add(nums[l]); temp.add(nums[r]); if(st.add(temp)) ans.add(temp); l++; if(nums[l] == nums[l-1]) l++; }else if(nums[index]+nums[l]+nums[r] < 0){ l++; if(nums[l] == nums[l-1]) l++; } else if(nums[index]+nums[l]+nums[r] > 0){ r--; if(nums[r] == nums[r+1]) r--; } } index++; } return ans; } }
//優化後的程式碼
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new LinkedList<>();
Arrays.sort(nums);
int index = 0;
if(nums.length==0) return ans;
while(true){
if(index > nums.length - 3 || nums[index] > 0 ) break;
int l = index+1;
int r = nums.length-1;
while(l < r){
if(nums[index]+nums[l]+nums[r] == 0){
LinkedList<Integer> temp = new LinkedList<>();
temp.add(nums[index]);
temp.add(nums[l]);
temp.add(nums[r]);
ans.add(temp);
l++;
//去重
while(l<nums.length && nums[l] == nums[l-1]) l++;
}else if(nums[index]+nums[l]+nums[r] < 0){
l++;
//去重
while(l < nums.length && nums[l] == nums[l-1]) l++;
}
else if(nums[index]+nums[l]+nums[r] > 0){
r--;
//去重
while(r > -1 && nums[r] == nums[r+1]) r--;
}
}
index++;
//去重
while(index < nums.length && nums[index] == nums[index-1]) index++;
}
return ans;
}
}
3.四數之和
題目連結
題目描述
解題思路
1.暴力,四重for迴圈
2.排序+雙指標
AC程式碼
//暴力法,四重for迴圈
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> ans = new LinkedList<>();
Set<LinkedList> s = new HashSet<>();
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++){
for(int j = i+1; j < nums.length; j++){
for(int k = j + 1; k < nums.length; k++){
for(int g = k + 1; g < nums.length; g++){
if(nums[i]+nums[j]+nums[k]+nums[g]==target){
LinkedList temp = new LinkedList<>();
temp.add(nums[i]);
temp.add(nums[j]);
temp.add(nums[k]);
temp.add(nums[g]);
if(s.add(temp)) ans.add(temp);
}
}
}
}
}
return ans;
}
}
//排序+雙指標
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> ans = new LinkedList<>();
Set<List<Integer>> hashset = new HashSet<>();
Arrays.sort(nums);
if(nums.length < 4) return ans;
for(int i = 0; i < nums.length; i++){
for(int j = i + 1; j < nums.length; j++){
int l = j + 1;
int r = nums.length-1;
while(l < r){
if(nums[i]+nums[j]+nums[l]+nums[r] == target){
LinkedList temp = new LinkedList<>();
temp.add(nums[i]);
temp.add(nums[j]);
temp.add(nums[l]);
temp.add(nums[r]);
if(hashset.add(temp)) ans.add(temp);
l++;
}else if(nums[i]+nums[j]+nums[l]+nums[r] < target){
l++;
}else if(nums[i]+nums[j]+nums[l]+nums[r] > target){
r--;
}
}
}
}
return ans;
}
}
n數之和
在三數和四數之和中,我們利用排序+雙指標都直接把暴力方法的時間複雜度降了一級,除了二數之和外,n數之和都可以採用排序+雙指標的解決方法。