題目:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
翻譯:
給定一組整數,兩個數字的返回索引,它們的和會等於一個特定的數。
您可能假設每個輸入都有一個解決方案,但是你不能使用同一個元素兩次。(好吧一開始英語弱雞的我也沒懂,后來編程的時候想到的解釋:同一個數不能通過自加而得到目標數)
第一遍編寫:74ms
class Solution { public int[] twoSum(int[] nums, int target) { Set<Integer> resultSet = new HashSet<Integer>(); for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j < nums.length; j++) { // 就是這里想到一個數自加,所以應該從i+1開始。 if (nums[i] + nums[j] == target) { resultSet.add(i); resultSet.add(j); } } } int[] result = new int[resultSet.size()]; for (int i = 0; i < result.length; i++) { result[i] = (Integer) resultSet.toArray()[i]; } return result; } }
好吧,第一次刷題,隱隱小激動,一開始確實沒看清題目。。
看清題目后第一次:46ms 時間復雜度:O(n2)
1 class Solution { 2 public int[] twoSum(int[] nums, int target) { 3 int[] result = new int[2]; 4 for (int i = 0; i < nums.length; i++) { 5 for (int j = i + 1; j < nums.length; j++) { 6 if (nums[i] + nums[j] == target) { 7 result[0] = i; 8 result[1] = j; 9 break; 10 } 11 } 12 } 13 return result; 14 } 15 }
自己看着都覺得菜的很。。竟然用了嵌套for。。
下面是參考答案:12ms 時間復雜度:O(n)
1 public int[] twoSum(int[] nums, int target) { 2 Map<Integer, Integer> map = new HashMap<Integer, Integer>(); 3 int[] result = new int[2]; 4 5 for (int i = 0; i < nums.length; i++) { 6 if (map.containsKey(target - nums[i])) { 7 result[0] = map.get(target - nums[i]); 8 result[1] = i; // 此時 i 對應的元素還沒有放進去。 9 return result; 10 } 11 map.put(nums[i], i); 12 } 13 return result; 14 }
原來用了HashMap來定位兩者有某種關系的數。。快了一倍多。。
結論:
當需要使用嵌套for循環來查找或者定位的時候,盡量優先考慮是否能使用Map(存儲每一個值作為key,相應下標作為value)
使用 map.containsKey(__) 方法來進行定位
2018-1-8更新:今天做到3sum想到和這里的2sum聯系,
3sum:求所有的相加為目標值的數組,
2sum:一旦查找到相加為目標值的倆數組則直接返回倆下標,程序直接結束(僅一組)
現在將2sum的題目改為如下:
給定一個n個整數的數組,在數組中找到和為目標值的所有唯一的兩個元素組合【注意不是求下標】
注意:答案集不能包含重復的雙胞胎。
測試用例:{1,4,-1,2,-1,0} 1 結果:[[0, 1], [-1, 2]]
{3,2,3,4,1,4,5,5} 8 結果:[[4, 4], [3, 5]]
Code1:時間復雜度:O(N) 【其實應該是Arrays.sort(num)的復雜度】
1 public static List<List<Integer>> twoSumAll(int[] num, int target) { 2 Arrays.sort(num); // 將所有相同元素挨在一起 3 List<List<Integer>> res = new LinkedList<List<Integer>>(); 4 Set<Integer> set = new HashSet<Integer>(); // 不需要下標,並且需要元素唯一,所以采用Set 5 for (int i = 0; i < num.length; i++) { 6 if (set.contains(target - num[i])) { 7 res.add(Arrays.asList(target - num[i], num[i])); 8 set.remove(target - num[i]); // 防止后續重復元素繼續利用此值(因為已經排序,后面不會再有前面的值,不需要再remove(num[i])) 9 } 10 set.add(num[i]); 11 } 12 return res; 13 }
此方法仍然采用上面2sum的思想,利用contains()函數減少一次循環,注意Set的remove使答案避免了”雙胞胎數組“
下面采用第15題的3sum的‘’雙指針相向移動‘’的思想進行算法編寫:
Code2:時間復雜度:O(N) 【其實應該是Arrays.sort(num)的復雜度】
1 public static List<List<Integer>> twoSumAll2(int[] num, int target) { 2 Arrays.sort(num); 3 List<List<Integer>> res = new LinkedList<List<Integer>>(); 4 int left = 0; 5 int right = num.length - 1; 6 while (left < right) { 7 if (num[left] + num[right] == target) { 8 res.add(Arrays.asList(num[left], num[right])); 9 while (left < right && num[left] == num[left+1]) left++; 10 while (left < right && num[right] == num[right-1]) right--; 11 left++; 12 right--; 13 } else if (num[left] + num[right] < target) { 14 left++; 15 } else { 16 right--; 17 } 18 } 19 return res; 20 }
雙指針相向移動:利用了 已排序數組 和 所查找的兩個數的和為定值 這兩個性質
這兩個性質如果聯合一起用的話………………duang!!!
在已排序數組兩端指針所指數之和如果小於目標值,只需要移動左邊的指針,否則只需要移動右邊的指針
【還看不懂可以自己打草稿試試,或者看我這個博客:LeetCode第[15]題(Java):3Sum 標簽:Array】
結論:在求和為目標數時,求下標——HashMap;
求具體組合值——前后指針;