文章均为本人技术笔记,转载请注明出处:
[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)$