目录
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 合并两个有序数组
这种题目比较简单,如果要返回的是具体的数,那么可以直接排序然后利用二分查找来做,如果要返回的是数组的下标,那么可以用Map存储数组值和下标。
public int[] twoSum2(int[] nums, int target) {
Map 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[] {};
}
同样可以参考两数之和的解法,可以先固定一个数,然后利用二分查找的方法来查找,这里我们要特别注意的一个问题就是去重问题,由于这里是返回的值,可以对数组先排序,其具体代码如下
public static List> threeSum(int[] nums) {
List> 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 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;
}
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 (temptarget){
k--;
}else if(curSum
/*
* 同样可以固定两个数,然后剩下的利用双指针来解决
*/
public static List> fourSum(int[] nums, int target) {
List> result = new ArrayList<>();
if(nums.length==0||nums==null) {
return result;
}
Arrays.sort(nums);
for(int i=0;i 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(lowtarget-curSum) {
high--;
}else {
low++;
}
}
/*while(j
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
//利用回溯算法
public static List> combinationSum(int[] candidates, int target) {
List> 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 list, List> 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
数组中每一个数字只能使用一次
//利用回溯算法
public static List> combinationSum(int[] candidates, int target) {
List> 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 list, List> 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
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
public static List> combinationSum3(int k, int n) {
List> 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> lists, List 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);
}
}
输入描述:
输入为两行: 第一行为两个正整数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=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]);
}
public class MaxSubArray{
/*
利用动态规划去做
*/
public int maxSubArray(int[] nums){
int[] dp=new int[nums.length];
dp[0]=nums[0];
for(int i=0;imax){
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));
}
}
public static List letterCombinations(String digits) {
List 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 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
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-1){
return true;
}
}
return true;
}
}
题目描述
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
示例:
输入: 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--];
}
}
}