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. 循环赛日常表算法(N可为奇数和偶数)

    一. 实验题目 设有n位选手参加网球循环赛,循环赛共进行n-1天,每位选手要与其他n-1位选手比赛一场,且每位选手每天必须比赛一场,不能轮空.试按此要求为比赛安排日程. 二.实验目的 1.深刻理解并掌 ...

  2. 利用Linux系统生成随机密码的8种方法

    Linux操作系统的一大优点是对于同样一件事情,你可以使用高达数百种方法来实现它.例如,你可以通过数十种方法来生成随机密码.本文将介绍生成随机密码的十种方法. 1. 使用SHA算法来加密日期,并输出结 ...

  3. The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path(Myeclipse添加Server Library)

    网上找练习的项目导入到myeclipse项目发现每个JSP 出现错误The superclass "javax.servlet.http.HttpServlet" was not ...

  4. Linux squid 缓存服务器

    一.简介 代理服务器英文全称是Proxy Server,其功能就是代理网络用户去取得网络信息. Squid是一个缓存Internet 数据的软件,其接收用户的下载申请,并自动处理所下载的数据.当一个用 ...

  5. Network of Schools---poj1236(强连通分量)

    题目链接 题意:学校有一些单向网络,现在需要传一些文件 求:1,求最少需要向几个学校分发文件才能让每个学校都收到, 2,需要添加几条网络才能从任意一个学校分发都可以传遍所有学校. 解题思路(参考大神的 ...

  6. C++循环的内存释放问题?

    针对http://wenku.baidu.com/view/56d732ee856a561252d36ff2.html的内容测试一下. #include "A.h" #includ ...

  7. HDU5023:A Corrupt Mayor's Performance Art(线段树区域更新+二进制)

    http://acm.hdu.edu.cn/showproblem.php?pid=5023 Problem Description Corrupt governors always find way ...

  8. Saltstack数据系统

    1.grainsgrains 是在 minion(客户端)启动时收集到的一些信息,比如操作系统类型.网卡ip等.强调是minion启动时收集到的数据,所以如果改了什么硬件啥的,要重启minion才能收 ...

  9. android studio gradle 国内代理

    使用阿里云的国内镜像仓库地址,就可以快速的下载需要的文件 修改项目根目录下的文件 build.gradle : buildscript { repositories { maven{ url 'htt ...

  10. VS2010/MFC编程入门之二十九(常用控件:列表视图控件List Control 下)

    上一节是关于列表视图控件List Control的上半部分,简单介绍了列表视图控件,其通知消息的处理和有关结构体的定义.本节继续讲解下半部分,包括列表视图控件的创建.CListCtrl类的主要成员函数 ...