两数之和
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
示例:
给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
思路:第一种方法是拿Map存储值和下标,然后遍历数组,用containskey函数判断是否存在target-当前值的数。
第二种方法可以用双指针做。但是因为要求输出下标,需要保存下标和值然后排序。
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] result = new int[2];
Map<Integer,Integer> m = new HashMap<Integer, Integer>();
for(int i=0;i<nums.length;i++){
m.put(nums[i],i);
}
for(int i=0;i<nums.length;i++){
int t = target-nums[i];
if(m.containsKey(t) && m.get(t)!=i){
result[0]=i;
result[1]=m.get(t).intValue();
break;
}
}
return result;
}
}
三数之和
给定一个包含 n 个整数的数组 nums
,判断 nums
中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
思路:双指针法。需要注意的是去重。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
Set<List<Integer>> s = new HashSet<List<Integer>>();
if(nums.length<3)
return res;
Arrays.sort(nums);
int sum = 0;
for(int i=0;i<nums.length-2;i++){
if(i>0&&nums[i]==nums[i-1])
continue;
int left = i+1;
int right = nums.length-1;
while(left<right){
sum = nums[i]+nums[left]+nums[right];
if(sum==0){
List<Integer> l = new ArrayList<Integer>();
l.add(nums[i]);
l.add(nums[left]);
l.add(nums[right]);
if(!s.contains(l)){
res.add(l);
s.add(l);
}
left++;
right--;
}else if(sum<0)
left++;
else if(sum>0)
right--;
}
}
return res;
}
}
K数之和
即从长度为n的数组里选出k个数使和为固定值sum(k为定值)
首先,我们排除递归。选用二进制的方法处理。(较为暴力,待优化)
比如数组中有10个数字 比如{-10,45,35,99,10,6,9,20,17,18} , sum为35,用二进制的0000000000~1111111111代表某个数字是否被选中,如果数字是0101010101代表45,99,6,20,18五个数字被选出来了。接着我们只需要计算着五个数是否等于我们要最终需要sum。
import java.util.*;
public class Test{
public static void main(String[] args){
int[] nums = {1,2,3,4,5,-1,2,3,5,4,1};
List<List<Integer>> l = CalSum(nums,6,3);
for(int i=0;i<l.size();i++)
System.out.println(l.get(i));
}
public static int NumOf1(int num){
int count = 0;
while (num!=0){
num = num & (num - 1);
count++;
}
return count;
}
public static List<List<Integer>> CalSum(int[] nums, int result, int k){
List<List<Integer>> res = new ArrayList<List<Integer>>();
Set<List<Integer>> s = new HashSet<List<Integer>>();
int[] n = nums;
Arrays.sort(n); //排序后用Set判断解决去重问题
int len = n.length;
int bit = 1 << len;
for (int i = 1; i < bit; i++){ //从1循环到2^N
int sum = 0;
List<Integer> tmp = new ArrayList<Integer>();
if (NumOf1(i) == k){
for (int j = 0; j < len; j++){
if ((i & (1 << j)) != 0){ //用i与2^j进行位与运算,若结果不为0,则表示第j位不为0,从数组中取出第j个数
sum += n[j];
tmp.add(n[j]);
}
}
if (sum == result){
if(!s.contains(tmp)){
res.add(tmp);
s.add(tmp);
}
}
}
}
return res;
}
}
变形:不定项数之和为Sum
题目描述
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。
输入描述:
输入为两行: 第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000) 第二行为n个正整数A[i](32位整数),以空格隔开。
输出描述:
输出所求的方案数
输入
5 15 5 5 10 2 3
输出
4
这里我们需要用到动态规划。
设dp[i][j]表示前i个数字中和为j个组合数(下标从1开始),则递推公式为:
当j<a[i]时,说明a[i]是没有用的,加上会超过j,因此使用前i个数和为j的组合数等于前i-1个数和为j(不使用第i个数时)的组合数
当j>=a[i]时,dp[i][j]=使用前i-1个数和为j的组合数+使用前i-1个数和为j-a[i]的组合数(j-a[i]+a[i]=j)
import java.util.*;
public class Test {
public static int n=0;
public static long calSum1(int a[],int sum){
long dp[][]=new long[n+1][sum+1];
dp[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=sum;j++){
if(j>=a[i])
dp[i][j]=dp[i-1][j-a[i]]+dp[i-1][j];
else
dp[i][j]=dp[i-1][j];
}
}
return dp[n][sum];
}
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
n=in.nextInt();
int a[]=new int[n+1];
int sum=in.nextInt();
for(int i=1;i<=n;i++)
a[i]=in.nextInt();
System.out.println(calSum1(a,sum));
}
}
待解决:输出所有可能的组合情况。
https://blog.youkuaiyun.com/a987073381/article/details/52016960
https://blog.youkuaiyun.com/ccj_ok/article/details/77394103
https://blog.youkuaiyun.com/zhengjihao/article/details/77921617