数组问题汇总

目录

1.数组和问题

1.1 两个数之和为k

1.2 三数之和为0

1.3 最接近的三数之和

1.4 四个数之和

1.5 和为k的子数组问题I

1.6 和为k的子数组问题I

1.7 和为k的子数组问题III

1.8 数字和为Sum的方法数

1.9 最大子数组和问题

2.其他数组问题

2.1 电话号码的组合

2.2 跳跃问题

2.3 合并两个有序数组

 


1.数组和问题

1.1 两个数之和为k

这种题目比较简单,如果要返回的是具体的数,那么可以直接排序然后利用二分查找来做,如果要返回的是数组的下标,那么可以用Map存储数组值和下标。

public int[] twoSum2(int[] nums, int target) {
	    Map<Integer, Integer> map = new HashMap<>();
	    for (int i = 0; i < nums.length; i++) {
	        int complement = target - nums[i];
	        if (map.containsKey(complement)) {
	            return new int[] { map.get(complement), i };
	        }
	        map.put(nums[i], i);
	    }
	    return new int[] {};
	}

1.2 三数之和为0

同样可以参考两数之和的解法,可以先固定一个数,然后利用二分查找的方法来查找,这里我们要特别注意的一个问题就是去重问题,由于这里是返回的值,可以对数组先排序,其具体代码如下

public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();        
        if (nums != null && nums.length > 2) {
            // 先对数组进行排序
            Arrays.sort(nums);
            // i表示假设取第i个数作为结果
            for (int i = 0; i < nums.length - 2; i++) {
                if (i != 0 && nums[i] == nums[i-1]) continue;               
                int j = i + 1;        
                int k = nums.length - 1;
                while (j < k) {
                    // 如果找到满足条件的解
                    if (nums[j] + nums[k] == -nums[i]) {
                        // 将结果添加到结果含集中
                        List<Integer> list = new ArrayList<>(3);
                        list.add(nums[i]);
                        list.add(nums[j]);
                        list.add(nums[k]);
                        result.add(list);

                        // 移动到下一个位置,找下一组解
                        k--;
                        j++;

                        // 从左向右找第一个与之前处理的数不同的数的下标
                        while (j < k && nums[j] == nums[j - 1]) {
                            j++;
                        }
                        // 从右向左找第一个与之前处理的数不同的数的下标
                        while (j < k && nums[k] == nums[k + 1]) {
                            k--;
                        }
                    }
                    // 和大于0
                    else if (nums[j] + nums[k] > -nums[i]) {
                        k--;
                      
                    }
                    // 和小于0
                    else {
                        j++;
                       
                    }
                }              
            }
        }

        return result;
    }

1.3 最接近的三数之和

public static int threeSumClosest(int[] nums, int target) {
		Arrays.sort(nums);
		int min=Integer.MAX_VALUE;
		int closeSum=0;
        for (int i = 0; i < nums.length - 2; i++) {
            int j = i + 1;          
            int k = nums.length - 1;           
            while (j < k) {
            	int curSum=nums[j] + nums[k]+nums[i];
            	int temp=Math.abs(curSum-target);
                // 如果找到满足条件的解
                if (temp<min) {                    
                    closeSum=curSum;
                	min=temp; 
                   
                }else if(curSum>target){
                	k--;
                }else if(curSum<target){
                	j++;
                }else {
                	return curSum;
                }
            }          
        }
        return closeSum;
    }

1.4 四个数之和

/*
	 * 同样可以固定两个数,然后剩下的利用双指针来解决
	 */
	public static List<List<Integer>> fourSum(int[] nums, int target) {
		List<List<Integer>> result = new ArrayList<>();
		if(nums.length==0||nums==null) {
        	return result;
        }
		Arrays.sort(nums);
		for(int i=0;i<nums.length-3;i++) {
			if (i != 0 && nums[i] == nums[i-1]) continue;
			for(int j=i+1;j<nums.length-2;j++) {
				if (j != i+1 && nums[j] == nums[j-1]) continue;
				int curSum=nums[i]+nums[j];
				int low=j+1;
				int high=nums.length-1;
				while(low<high) {
					if(nums[low]+nums[high]==target-curSum) {
						List<Integer> list = new ArrayList<>(4);
						list.add(nums[i]);
						list.add(nums[j]);
						list.add(nums[low]);
						list.add(nums[high]);
						result.add(list);
						low++;
						high--;
						//去重 
						while(low<high&&nums[low]==nums[low-1]) {
							low++;
						}
						while(low<high&&nums[high]==nums[high+1]) {
							high--;
						}
					}else if(nums[low]+nums[high]>target-curSum) {
						high--;
					}else {
						low++;
					}					
				}
				/*while(j<nums.length-2&&nums[j]==nums[j+1]) {
					j++;
				}*/				
			}
			/*while(i<nums.length-3&&nums[i]==nums[i+1]) {
				i++;
			}*/
		}
		
		return result;
    }

1.5 和为k的子数组问题I

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

//利用回溯算法
	public static List<List<Integer>> combinationSum(int[] candidates, int target) {
		List<List<Integer>> lists=new ArrayList<>();
		if(candidates.length==0) {
        	return lists;
        }
		dfs(candidates,0,new ArrayList<>(),lists,target);
		return lists;
		
    }

	private static void dfs(int[] candidates, int index, List<Integer> list, List<List<Integer>> lists, int target) {
		// TODO Auto-generated method stub
		//满足条件退回到上一步
		if(target==0) {
			lists.add(new ArrayList<>(list));
			return;
		}
		if(target<0) {
			return;
		}
		for(int i=index;i<candidates.length;i++) {
			list.add(candidates[i]);
			dfs(candidates,i,list,lists,target-candidates[i]);
			list.remove(list.size()-1);
		}
		
	}
	

1.6 和为k的子数组问题I

数组中每一个数字只能使用一次

//利用回溯算法
		public static List<List<Integer>> combinationSum(int[] candidates, int target) {
			List<List<Integer>> lists=new ArrayList<>();
			if(candidates.length==0) {
	        	return lists;
	        }
			Arrays.sort(candidates);
			dfs(candidates,0,new ArrayList<>(),lists,target);
			return lists;
			
	    }

		private static void dfs(int[] candidates, int index, List<Integer> list, List<List<Integer>> lists, int target) {
			// TODO Auto-generated method stub
			//满足条件退回到上一步
			if(target==0) {
				if(!lists.contains(list))
					lists.add(new ArrayList<>(list));
				return;
			}
			if(target<0) {
				return;
			}
			for(int i=index;i<candidates.length;i++) {
				list.add(candidates[i]);
				dfs(candidates,i+1,list,lists,target-candidates[i]);
				list.remove(list.size()-1);
			}
			
		}
		

1.7 和为k的子数组问题III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

 public static List<List<Integer>> combinationSum3(int k, int n) {
		 List<List<Integer>> lists=new ArrayList<>(); 
		 if(k==0||n==0)
	    	 return lists;
		 
	     dfs(k,n,1,lists,new ArrayList<>());  
	     return lists;
	 }

	private static void dfs(int k,int target, int index, List<List<Integer>> lists, List<Integer> list) {
		// TODO Auto-generated method stub
		if(list.size()==k&&target==0) {
			if(!lists.contains(list))
				lists.add(new ArrayList<>(list));
			return;
		}
		if(target<0) {
			return ;
		}
		for(int i=index;i<=9;i++) {
			
			list.add(i);
			dfs(k,target-i,i+1,lists,list);
			list.remove(list.size()-1);
		}
	
	}

1.8 数字和为Sum的方法数

输入描述:

输入为两行: 第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000) 第二行为n个正整数A[i](32位整数),以空格隔开。

输出描述:

输出所求的方案数

示例1

输入

5 15

5 5 10 2 3

输出

4

刚开始看到这个题的时候,第一眼就想到用递归来做,用回溯来做,说实话递归确实好写,但复杂度特别高为O(N^N),测试了一下,确实通不过

后来在评论区里看到全都是用dp动态规划来做的,分析了一下,这个有点类似于0-1背包问题,就是要么取这个数,要么不取这个数,将这两种方案数加起来就是总的方案数,0-1背包问题的公公式如下:

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

                                     
初始条件:dp[i][0]=1,i=0,1,2,…,ndp[i][0]=1,i=0,1,2,…,n. 
初始条件是指,如果和为0,那么一个也不选,不选也是一种选择,所以是1. 

以本题中的例子为例,借用评论区里面的一张示意图,得到的动态规划矩阵如下:

这里由于要对应下标,所以所有的小下标都是从1开始的,先看第一列,表示的是取前i个数能组成sum为0的方法数,只有一种,就是什么都不取,再来看第一行(除dp[0][0]以外)表示的是取前0个数能组成sum为0~15的方法数,当然全部是0(即这种是不可能的),这些位置的值初始化完成之后,开始从第二行第二列遍历数组,其核心公式为dp[i][j]=dp[i-1][j]+dp[i-1][j-num[i]];以dp[5][5]为例来说吧,图中dp[5][5]=3,

1.假设不取.num[5]=3这个数,那么利用前5个数组成和为5的方案有多少种呢?就是前4个数组成和为5的方案数,也就是dp[4][5]的值,dp[4][5]=2;

2.假设取num[5]=3这个数,那么利用前5个数组成和为5的方案有多少种呢?我们只需要找到利用前5个数组成和为2的方案数就可以了,加上这个3肯定就是组成sum为5了,而dp[5][2]=1。将这两种方案加起来就是3种了。

代码如下:

public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int sum = sc.nextInt();
        int[] num = new int[n+1];
 
        for (int i = 1; i <=n; i++) {
            num[i] = sc.nextInt();
        }
 
        long[][] dp=new long[n+1][sum+1];
        for(int i=0;i<n;i++){
            dp[i][0]=1;    //表示取前i个数字组成和为0的方法数为1(就是什么都不取)
        }
        for(int j=0;j<sum;j++){
            dp[0][j]=0;  //表示取0个数组成sum的方法数为0(就是这是不可能的)
        }
 
        for(int i=1;i<=n;i++){
            for(int j=1;j<=sum;j++){
                if(j>=num[i]){
                    dp[i][j]=dp[i-1][j]+dp[i-1][j-num[i]];
                }else{
                    dp[i][j]=dp[i-1][j];
                }
            }
        }
        System.out.print(dp[n][sum]);
 
    }

1.9 最大子数组和问题

public class MaxSubArray{
	/*
	利用动态规划去做
	 */
	public int maxSubArray(int[] nums){
		int[] dp=new int[nums.length];
		dp[0]=nums[0];
		for(int i=0;i<nums.length-1;i++){
			dp[i+1]=Math.max(dp[i]+nums[i+1],nums[i+1]);
		}

		//遍历dp[]找到最大值
		int max=dp[0];
		for(int i=0;i<dp.length;i++){
			if(dp[i]>max){
				max=dp[i];
			}
		}
		return max;
	}

	public static void main(String[] args) {
		MaxSubArray msa=new MaxSubArray();
		int[] nums=new int[]{-2,1,-3,4,-1,2,1,-5,4};
		System.out.print(msa.maxSubArray(nums));
	}
}

2.其他数组问题

2.1 电话号码的组合

public static List<String> letterCombinations(String digits) {
	     List<String> list=new ArrayList<>(); 
		 String[] str=new String[] {" "," ","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"}; 
	     if(digits.length()==0||digits==null) {
	    	 return list;
	     }
	 
	     dfs(digits,str,0,list,new StringBuffer());
	     return list;
	       
	 }

	private static void dfs(String digits, String[] str, int index, List<String> list,StringBuffer res) {
		// TODO Auto-generated method stub
		if(index==digits.length()) {
			list.add(res.toString());
			return;
		}
		String phone=str[digits.charAt(index)-'0'];
		for(int i=0;i<phone.length();i++) {
			res.append(phone.charAt(i));
			dfs(digits,str,index+1,list,res);
			res.deleteCharAt(res.length()-1);			
		}
		
	}

2.2 跳跃问题

public class CanJump{
	public static boolean canJump(int[] nums){
        if(nums.length==1){
            return true;
        }
        //表示从i位置出发能到达的最远位置
        int far=0;
        for(int i=0;i<nums.length;i++){
        	 //返回false的情况
            if(far<i){
                return false;
            }
            far=Math.max(nums[i]+i,far);
            if(far>=nums.length-1){
                return true;
            }
           
        }
        return true;
    }

}

2.3 合并两个有序数组

题目描述

给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 使得 num1 成为一个有序数组。

说明:

  • 初始化 nums1 和 nums2 的元素数量分别为 m 和 n
  • 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

输出: [1,2,2,3,5,6]
 public class Merge{

 	/*
 	一开始准备从前往后比较,发现不合适,然后参考了一下可以考虑从后往前比较
 	 */

 	public void merge(int[] nums1, int m, int[] nums2, int n) {
 		int len=m+n-1;   //表示总的元素个数
 		if(m<1){
 			while(len>=0){
 				nums1[len--]=nums2[--n];
 			}
 			
 		}

 		int index1=m-1;
 		int index2=n-1;
 		
 		while(index1>=0&&index2>=0){
 			if(nums1[index1]>nums2[index2]){
 				nums1[len--]=nums1[index1--];

 			}
 			else{
 				nums1[len--]=nums2[index2--];
 			}
 			
 		}
 		while(index1>=0){
 			nums1[len--]=nums1[index1--];
 		}
 		while(index2>=0){
 			nums1[len--]=nums2[index2--];
 		}      
    }

 }
 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值