算法题 两/三/K数之和为Sum+不定项和为Sum

两数之和

给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。

你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

示例:

给定 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个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。

输入描述:

输入为两行:

第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)

第二行为n个正整数A[i](32位整数),以空格隔开。

输出描述:

输出所求的方案数
示例1

输入

5 15
5 5 10 2 3

输出

4

这里我们需要用到动态规划。

设dp[i][j]表示前i个数字中和为j个组合数(下标从1开始),则递推公式为: 

dp[i][j]={dp[i1][j]dp[i1][j]+dp[i1][ja[i]]j<a[i]j>=a[i]

当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.csdn.net/a987073381/article/details/52016960

https://blog.csdn.net/ccj_ok/article/details/77394103

https://blog.csdn.net/zhengjihao/article/details/77921617

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值