2018-04-22 19:59:52

Sum系列的问题是Leetcode上的一个很经典的系列题,这里做一个简单的总结。

  • 167. Two Sum II - Input array is sorted

问题描述:

问题求解:

对于已排序的问题,可以使用双指针在O(n)的时间复杂度内完成求解。

    // 已排序数组,返回indices
public int[] twoSum(int[] numbers, int target) {
int i = 0;
int j = numbers.length - 1;
while (i < j) {
if (target > numbers[i] + numbers[j]) i++;
else if (target < numbers[i] + numbers[j]) j--;
else break;
}
return new int[]{i + 1, j + 1};
}
  • 1. Two Sum

问题描述:

问题求解:

可以使用数据结构中的hash来很高效的解决,具体来说,我们可以建立一个hashmap,用来保存数值和其index,遍历数组,如果说hashmap中存在target - nums[i],由于题目中明确了只有唯一的解,因此就可以直接确定结果,将这两个数的index返回即可。

    public int[] twoSum3(int[] numbers, int target) {
int[] result = new int[2];
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < numbers.length; i++) {
if (map.containsKey(target - numbers[i])) {
result[1] = i + 1;
result[0] = map.get(target - numbers[i]);
break;
}
map.put(numbers[i], i + 1);
}
return result;
}
  • 653. Two Sum IV - Input is a BST

问题描述:

Given a Binary Search Tree and a target number, return true if there exist two elements in the BST such that their sum is equal to the given target.

问题求解:

方法一、很容易想到的是二叉搜索树的中序遍历是一个有序数列,如果我们采用中序遍历一次并保存下来,那么问题就变成了上述的已排序数组求two sum的问题。

代码时间复杂度为O(n)。

    ArrayList<Integer> ls = new ArrayList<>();
public boolean findTarget(TreeNode root, int k) {
inOrder(root);
boolean res = false;
int i = 0;
int j = ls.size() - 1;
while (i < j) {
if (ls.get(i) + ls.get(j) > k) j--;
else if (ls.get(i) + ls.get(j) < k) i++;
else {
res = true;
break;
}
}
return res;
} void inOrder(TreeNode root) {
if (root != null) {
inOrder(root.left);
ls.add(root.val);
inOrder(root.right);
}
}

方法二、递归遍历,每次递归到某个数就对target - nums[i]进行查找,值得注意的是,在查找过程中要特别注意不能是当前的数,因为同一个数只能出现一次,因此在传参的时候要把当前的结点信息传进去。

代码时间复杂度从理论上来说应该是O(nlogn)。但由于剪枝效应的存在,所以在实际的运行上还是比较高效的。

    public boolean findTarget(TreeNode root, int k) {
return dfs(root, root, k);
} private boolean dfs(TreeNode root, TreeNode cur, int k) {
if(cur == null) return false;
return search(root, cur, k-cur.val) || dfs(root, cur.left, k) || dfs(root, cur.right, k);
} private boolean search(TreeNode root, TreeNode cur, int target) {
if(root == null) return false;
if(target == root.val) return root != cur;
else if (target > root.val) return search(root.right, cur, target);
else return search(root.left, cur, target);
}
  • 15. 3Sum

问题描述:

问题求解:

主要的思想就是转化成Two Sum的问题,其中由于结果不能重复,所以我们需要提前对nums进行排序,在排序后,对先后相等的数就可以进行忽略处理了,这样就避免了重复的问题。另外,由于本题中的target = 0,那么在排序后的数组中如果其值大于0,那么也是可以直接排除可能性的,因为其值大于0,其后面的值也必然大于0,因此是不可能存在说三个正数的和为0的。

本题其实也是可以使用dfs + 回溯解决的,但是时间复杂度上会高不少。这里就不多讲解了。

    public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2; i++) {
if (i == 0 || (nums[i] <= 0 && nums[i] != nums[i - 1])) {
int sum = 0 - nums[i];
int l = i + 1;
int r = nums.length - 1;
while (l < r) {
if (nums[l] + nums[r] == sum) {
res.add(Arrays.asList(nums[i], nums[l], nums[r]));
while (l < r && nums[l] == nums[l + 1]) l++;
while (l < r && nums[r] == nums[r - 1]) r--;
l++;
r--;
}
else if (nums[l] + nums[r] < sum) {
while(l < r && nums[l] == nums[l + 1]) l++;
l++;
}
else {
while(l < r && nums[r - 1] == nums[r]) r--;
r--;
}
}
}
}
return res;
}
  • 16. 3Sum Closest

问题描述:

问题求解:

本质上和Three Sum是一样的。

    public int threeSumClosest(int[] nums, int target) {
int min = Integer.MAX_VALUE;
int res = Integer.MAX_VALUE;
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2; i++) {
if (i == 0 || nums[i] != nums[i - 1]) {
int sum = target - nums[i];
int l = i + 1;
int r = nums.length - 1;
while (l < r) {
if (nums[l] + nums[r] == sum) {
return target;
}
else if (nums[l] + nums[r] < sum) {
if (min > sum - (nums[l] + nums[r])) {
min = sum - (nums[l] + nums[r]);
res = nums[i] + nums[l] + nums[r];
};
while (l < r && nums[l + 1] == nums[l]) l++;
l++;
}
else {
if (min > nums[l] + nums[r] - sum) {
min = nums[l] + nums[r] - sum;
res = nums[i] + nums[l] + nums[r];
};
while (l < r && nums[r - 1] == nums[r]) r--;
r--;
}
}
}
}
return res;
}
  • 18. 4Sum

问题描述:

问题求解:

转化成Three Sum就好了。

    public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 3; i++) {
if (i == 0 || nums[i] != nums[i - 1]) {
for (int j = i + 1; j < nums.length - 2; j++) {
if (j == i + 1 || nums[j] != nums[j - 1]) {
int sum = target - nums[i] - nums[j];
int l = j + 1;
int r = nums.length - 1;
while (l < r) {
if (nums[l] + nums[r] == sum) {
res.add(Arrays.asList(nums[i], nums[j], nums[l], nums[r]));
while (l < r && nums[l + 1] == nums[l]) l++;
while (l < r && nums[r - 1] == nums[r]) r--;
l++;
r--;
}
else if (nums[l] + nums[r] < sum) {
while (l < r && nums[l + 1] == nums[l]) l++;
l++;
}
else {
while (l < r && nums[r - 1] == nums[r]) r--;
r--;
}
}
}
}
}
}
return res;
}
  • 416. Partition Equal Subset Sum

问题描述:

问题求解:

其实就是一个背包问题,这里就是在看能不能挑其中n个物品,使其和为sum/2。当然,首先sum应该是偶数,如果sum奇数,那么就可以直接返回结果。

    public boolean canPartition(int[] nums) {
int sum = 0;
for (int i : nums) sum += i;
if (sum % 2 != 0) return false;
sum /= 2;
boolean dp[] = new boolean[sum + 1];
dp[0] = true;
for (int num : nums) {
for (int i = sum; i >= num; i--) {
dp[i] = dp[i] || dp[i - num];
}
}
return dp[sum];
}
  • 494. Target Sum

问题描述:

问题求解:

方法一、第一个方法就是暴力搜索,回溯枚举。时间复杂度为指数级。

    public int findTargetSumWays(int[] nums, int S) {
if (nums.length == 0) return 0;
return helper(nums, 0, S);
} private int helper(int[] nums, int idx, int S) {
if (idx == nums.length) {
if (S == 0) return 1;
else return 0;
}
int res = 0;
res += helper(nums, idx + 1, S + nums[idx]);
res += helper(nums, idx + 1, S - nums[idx]);
return res;
}

方法二、这个方法很有技巧性,实际上是把原问题转化成了求部分和的问题。不妨设+部分和为P,-部分和为Q,则P - Q = S,又P + Q = sum,所以得到2P = S + sum。也就是说求解nums中部分和为(S + sum)/ 2的总个数。由于原问题中指出了数字非负性,所以这种方法是可行的。算法的时间复杂度为伪多项式时间复杂度。

必须要多sum 和 S 的大小进行判断,因为S的大小可能远超sum,这个时候如果不加判断会MLE。

    public int findTargetSumWays(int[] nums, int S) {
int sum = 0;
for (int i : nums) sum += i;
if (sum < S || sum + S < 0 || (sum + S) % 2 != 0) return 0;
return helper(nums, (sum + S) / 2);
} private int helper(int[] nums, int S) {
int[] dp = new int[S + 1];
dp[0] = 1;
for (int num : nums) {
for (int i = S; i >= num; i--) {
dp[i] += dp[i - num];
}
}
return dp[S];
}

Sum Problem的更多相关文章

  1. summary of k Sum problem and solutions in leetcode

    I found summary of k Sum problem and solutions in leetcode on the Internet. http://www.sigmainfy.com ...

  2. Subset sum problem

    https://en.wikipedia.org/wiki/Subset_sum_problem In computer science, the subset sum problem is an i ...

  3. HDu 1001 Sum Problem 分类: ACM 2015-06-19 23:38 12人阅读 评论(0) 收藏

    Sum Problem Time Limit: 1000/500 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total ...

  4. HD2058The sum problem

    The sum problem Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...

  5. Maxmum subsequence sum problem

    We have a lot of ways to solve the maximum subsequence sum problem, but different ways take differen ...

  6. HDU 2058 The sum problem(枚举)

    The sum problem Problem Description Given a sequence 1,2,3,......N, your job is to calculate all the ...

  7. NYOJ--927--dfs--The partial sum problem

    /* Name: NYOJ--927--The partial sum problem Author: shen_渊 Date: 15/04/17 19:41 Description: DFS,和 N ...

  8. 动态规划法(三)子集和问题(Subset sum problem)

      继续讲故事~~   上次讲到我们的主人公丁丁,用神奇的动态规划法解决了杂货店老板的两个找零钱问题,得到了老板的肯定.之后,他就决心去大城市闯荡了,看一看外面更大的世界.   这天,丁丁刚回到家,他 ...

  9. HDU 2058:The sum problem(数学)

    The sum problem Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  10. Problem-1001:Sum Problem

    Sum Problem Sample code : #include <stdio.h> int main() { int i,n; int sum; while(scanf(" ...

随机推荐

  1. AngularJS 指令的 Scope (作用域)

    参考:https://segmentfault.com/a/1190000002773689 每当一个指令被创建的时候,都会有这样一个选择,是继承自己的父作用域(一般是外部的Controller提供的 ...

  2. python调用C++之pybind11入门(相互调用)

    python调用C/C++有不少的方法,如boost.python, swig, ctypes, pybind11等,这些方法有繁有简,而pybind11的优点是对C++ 11支持很好,API比较简单 ...

  3. mybatis三剑客之mybatis-pagehelper分页插件

    这是pom.xml里的依赖: 后续再讲具体的使用

  4. Python 读取写入配置文件 —— ConfigParser

    Python 读取写入配置文件 —— ConfigParser Python 读取写入配置文件很方便,可使用内置的 configparser 模块:可查看源码,如博主本机地址: “C:/python2 ...

  5. [3D]1.绘制三角形

    作为一个.Net程序员学习3D开发好尴尬啊,因为不论是OpenGL还是Direct3D都是用C/C++开发的比较多.虽然有计划使用C++进行开发,但是平时还是C#使用的多.很少用C++做东西,如果仅仅 ...

  6. 【译】3 ways to define a JavaScript class

    本文真没啥难点,我就是为了检验我英语水平退化了没哈哈虽然我英语本来就渣翻译起来也像大白话.将原文看了一遍也码完翻译了一遍差不多一个小时,其中批注部分是自己的理解如有疏漏或误解还请指出感激不尽呐,比如J ...

  7. 安装WIN7时提示“缺少所需的CD/DVD驱动器设备驱动程序”

    同事机器重装Win7,先百度了一下不适合64bit,于是直接上32bit系统. BOIS设置DVD启动,把安装盘放在移动光驱里,开始安装. 在安装时出现下图错误:“缺少所需的CD/DVD驱动器设备驱动 ...

  8. 005-matlab2018a安装破解

    1.下载地址: 百度云下载链接:https://pan.baidu.com/s/1uTYAxVX1_Hx6nbsgf4W4kA 密码:asrw 官网下载地址: 2.解压. 3.双击setup.exe后 ...

  9. [LeetCode]160.Intersection of Two Linked Lists(2个链表的公共节点)

    Intersection of Two Linked Lists Write a program to find the node at which the intersection of two s ...

  10. liunx 命令行快捷键 常用命令

    常用指令 ls        显示文件或目录 -l           列出文件详细信息l(list) -a          列出当前目录下所有文件及目录,包括隐藏的a(all) mkdir     ...