文章均爲本人技術筆記,轉載請註明出處:
[1] https://segmentfault.com/u/yzwall
[2] blog.csdn.net/j_dark/java
題目大意:給出未排序數組nums
和指定目標target
,返回數組中兩數之和$= target$的組合元素下標[index1, index2]
, 要求下標從1
開始,並且$index1 < index2$,保證題目中有且只有1個可行解;segmentfault
解題思路:暴力二重循環求解;
複雜度分析:時間複雜度$O(n^2)$,空間複雜度$O(1)$數組
/** * 解法1:時間複雜度O(n^2),空間複雜度O(1) * 遍歷求兩數之和等於target,返回兩數下標(從1開始) * http://www.lintcode.com/zh-cn/problem/two-sum/ * @author yzwall */ class Solution { public int[] twoSum(int[] nums, int target) { int[] results = new int[2]; for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j < nums.length; j++) { if (nums[i] + nums[j] == target) { results[0] = i + 1; results[1] = j + 1; return results; } } } return results; } }
解題思路:耗費$O(n)$空間構造哈希表,遍歷數組每一個元素nums[i]
,哈希表對應存儲<target - nums[i], i>
,存儲nums[i]指望的「另外一半」,一旦哈希表中包含nums[i],表明「另外一半」早已存儲在哈希表中,直接返回便可;
複雜度分析:時間複雜度$O(n)$,空間複雜度$O(n)$less
/** * 解法2:HashMap求解,時間複雜度O(n),空間複雜度O(n) * 遍歷求兩數之和等於target,返回兩數下標(從1開始) * http://www.lintcode.com/zh-cn/problem/two-sum/ * @author yzwall */ class Solution { public int[] twoSum(int[] nums, int target) { HashMap<Integer, Integer> map = new HashMap<>(); int[] results = new int[2]; for (int i = 0; i < nums.length; i++) { if (map.containsKey(nums[i])) { results[0] = map.get(nums[i]) + 1; results[1] = i + 1; break; } map.put(target - nums[i], i); } return results; } }
解題思路:首先將數組排序(時間複雜度$O(nlog(n))$),而後經過雙指針i
和j
分別從數組兩頭同時遍歷,保存數組排序前的元素位置可以使用HashMap
保存(空間複雜度$O(n)$),也可用輔助類保存(空間複雜度$O(1)$);
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(n)$ or $O(1)$;優化
/** * 解法3:HashMap + 雙指針求解,時間複雜度O(nlogn),空間複雜度O(n) * 遍歷求兩數之和等於target,返回兩數下標(從1開始) * http://www.lintcode.com/zh-cn/problem/two-sum/ * @author yzwall */ class Solution { public int[] twoSum(int[] nums, int target) { HashMap<Integer, ArrayList<Integer>> map = new HashMap<>(); int[] results = new int[2]; // HashMap用於記錄排序前數組元素對應下標 for (int i = 0; i < nums.length; i++) { if (map.containsKey(nums[i])) { map.get(nums[i]).add(i); continue; } map.put(nums[i], new ArrayList<Integer>()); map.get(nums[i]).add(i); } int i = 0, j = nums.length - 1; // 排序後雙指針求解 Arrays.sort(nums); while (i < j) { if (nums[i] + nums[j] == target) { int index1 = map.get(nums[i]).get(0); // 重複元素已經訪問過一次,從對應位置列表中剔除 map.get(nums[i]).remove(0); int index2 = map.get(nums[j]).get(0); // 保證results[0] < result[1] results[0] = Math.min(index1, index2) + 1; results[1] = Math.max(index1, index2) + 1; return results; } if (nums[i] + nums[j] > target) { j--; } else { i++; } } return results; } }
題目大意:給出未排序數組nums
和指定目標target
,返回數組中兩數之和$= target$的全部不重複組合數;.net
解題思路:數組排序後使用雙指針分別從起點和終點遍歷,若是存在$a + b = target$,則若是找到$a$全部組合方案,則$b$無需再找;
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度經過HashSet
去重,耗費額外空間$O(n)$(可優化到$O(1)$)指針
/** * 雙指針找兩數和等於target的不重複組合數目,時間複雜度O(n),空間複雜度O(n) * 求兩數之和等於target的全部不重複組合數目 * http://www.lintcode.com/zh-cn/problem/two-sum-unique-pairs/ * @author yzwall */ class Solution { public int twoSum6(int[] nums, int target) { int pairs = 0; if (nums == null || nums.length < 2) { return pairs; } Arrays.sort(nums); int i = 0; int j = nums.length - 1; HashSet<Integer> set = new HashSet<>(); while (i < j) { // 若是a + b = target, a找到後,b無需再找 while (i < j && set.contains(nums[i])) { i++; } while (i < j && set.contains(nums[j])) { j--; } if (i < j) { if (nums[i] + nums[j] == target) { set.add(nums[i]); set.add(nums[j]); pairs++; } else if (nums[i] + nums[j] > target) { j--; } else { i++; } } } return pairs; } }
題目大意:題目是LintCode_56的簡化版,解法1和解法2可直接使用;與解法1,2相比,解法3雙指針法充分利用數組已排序條件,時間複雜度下降到$O(n)$,空間複雜度下降到$O(1)$;code
題目大意:求出數組nums
中兩數之和$> target$的組合數目;blog
二重循環暴力求解;排序
解題思路:首先將數組nums
升序排序,雙指針$i$從起點開始,指針$j$從終點開始,一旦有:$$nums[i] + nums[j] > target, $$則因爲數組嚴格不遞減,$$\forall num\in[nums[i], nums[j]], num + nums[j] > target$$,所以執行pairs += (j - i)
,此時$nums[j]$全部方案搜索完畢,執行j--
;
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(1)$
/** * 解法2:雙指針法求解,時間複雜度O(logn),空間複雜度O(1) * 求兩數之和大於target的組合數目 * http://www.lintcode.com/en/problem/two-sum-greater-than-target/ * @author yzwall */ class Solution { public int twoSum2(int[] nums, int target) { int pairs = 0; if (nums == null || nums.length < 2) { return pairs; } Arrays.sort(nums); int i = 0; int j = nums.length - 1; while (i < j) { if (nums[i] + nums[j] > target) { pairs += j - i; // nums[j]全部方案求解完畢,j-- j--; } else { i++; } } return pairs; } }
題目大意:求出數組nums
中兩數之和$leqslant target$的組合數目;
二重循環暴力求解;
解題思路:首先將數組nums
升序排序,雙指針$i$從起點開始,指針$j$從終點開始,一旦有:$$nums[i] + nums[j] \leq target, $$則因爲數組嚴格不遞減,$$\forall num\in[nums[i], nums[j]], num + nums[j] \leq target$$,所以執行pairs += (j - i)
,此時$nums[i]$全部方案搜索完畢,執行i++
;
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(1)$
/** * 雙指針法求解,時間複雜度O(nlogn),空間複雜度O(1) * 求兩數之和小於等於target的全部組合數目 * http://www.lintcode.com/en/problem/two-sum-less-than-or-equal-to-target/ * @author yzwall */ class Solution { public int twoSum5(int[] nums, int target) { int pairs = 0; if (nums == null || nums.length < 2) { return pairs; } Arrays.sort(nums); int i = 0; int j = nums.length - 1; while (i < j) { // nums[i]的全部組合 = j - i if (nums[i] + nums[j] <= target) { pairs += j - i; i++; } else { j--; } } return pairs; } }
題目大意:求出數組nums
中兩數之和與target
的最近距離(非負);
解題思路:二重循環不斷迭代最小距離;
複雜度分析:時間複雜度$O(n^2)$,空間複雜度$O(1)$;
/** * 解法1:暴力時間複雜度O(n^2) * 求兩數之和最接近target,求最近距離 * http://www.lintcode.com/en/problem/two-sum-closest-to-target/ * @author yzwall */ class Solution { public int twoSumClosest(int[] nums, int target) { if (nums == null || nums.length < 2) { return target; } int min = Integer.MAX_VALUE; int temp; for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j < nums.length; j++) { temp = target - (nums[i] + nums[j]); min = Math.min(min, Math.abs(temp)); } } return min; } }
解題思路:首先將數組排序,雙指針分別從起點和終點遍歷,臨時距離
$$
temp = left | target - (nums[i] + nums[j]) right |
$$
不停與最短距離$min$比較迭代,$temp = 0$時直接返回;
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(1)$;
/** * 解法2:雙指針法求解,時間複雜度O(nlogn) * 求兩數之和最接近target,求最近距離 * http://www.lintcode.com/en/problem/two-sum-closest-to-target/ * @author yzwall */ class Solution { public int twoSumClosest(int[] nums, int target) { if (nums == null || nums.length < 2) { return target; } Arrays.sort(nums); int i = 0; int j = nums.length - 1; int min = Integer.MAX_VALUE; int temp; while (i < j) { temp = Math.abs(target - (nums[i] + nums[j])); if (temp == 0) { return 0; } min = Math.min(min, temp); if (nums[i] + nums[j] > target) { // 距離過大, j-- j--; } else { // 距離太小, i++ i++; } } return min; } }
二重循環暴力求解;
解題思路:首先將數組nums
升序排序,雙指針$i$從起點開始,指針$j$從終點開始,一旦有:$$nums[i] + nums[j] \leq target$$則因爲數組嚴格不遞減,$$\forall num\in[nums[i], nums[j]], num + nums[j] \leq target$$,所以執行pairs += (j - i)
,此時$nums[i]$全部方案搜索完畢,執行i++
;
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(1)$