链接
代码
枚举
枚举每个正整数为起点,判断以它为起点的序列和 sum 是否等于 target 即可,由于题目要求序列长度至少大于 2,所以枚举的上界为
public int[][] findContinuousSequence1(int target)
{
//让每个整数都做为起点尝试
List<int[]> list = new ArrayList<>(); //用 List 来接收符合条件的数组,返回时转换为二维数组返回
int sum = 0, upperBound = (target - 1) / 2; //等效于 t / 2 向下取整
//int sum = 0, upperBound = (int) Math.floor((target / 2))
for (int i = 1; i <= upperBound; i++) //外层循环的最大值为 (target - 1) / 2
{
for (int j = i; ; ++j) //连续正整数序列
{
sum += j;
if (sum > target)
{
sum = 0; //sum 大于 t ,重置sum 并退出内层循环
break;
} else if (target == sum)
{
//t == sum
//k == i --> j-i+2 为连续正整数的个数
int[] temp = new int[j - i + 1];
for (int k = i; k < j; k++)
{
temp[k - i] = k;
}
list.add(temp); //将数组添加到List 里
sum = 0; //重置sum
break;
}
}
}
return list.toArray(new int[list.size()][]);
}
枚举+数学优化
方法一在枚举每个正整数为起点判断的时候是暴力从起点开始累加 sum 和判断是否等于 target 。但注意到,如果我们知道起点 x 和终点 y ,那么 x 累加到 y 的和由求和公式可以知道是 ((x+y)* (y-x+1)) / 2,那么问题就转化为了是否存在一个正整数 y(y>x),满足等式
转换一下就变成
这是一个关于 y 的一元二次方程,其中 a=1,b=1,c=-x^2+x-2 * target 直接套用求根公式即可 O(1)O(1) 解得 y ,判断是否整数解需要满足两个条件:
- 判别式 b^2 - 4ac 开根需要为整数
- 最后的求根公式的分子需要为偶数,因为分母为 2
class Solution {
//a=1,b=1,c=-x^2+x-2×target
public int[][] findContinuousSequence(int target)
{
List<int[]> list = new ArrayList<>();
int uB = (target - 1) / 2;
for (int x = 1; x <= uB; x++)
{
//d = b^2 - 4*ac
long d = 1 - 4 * (x - 2L * target - ((long) x * x));
if (d < 0) //方程无解
{
continue; //进入下一次循环
}
int d_sqrt = (int) (Math.sqrt(d + 0.5)); //加上 0.5 防止精度丢失
if ((long) d_sqrt * d_sqrt == d && (d_sqrt - 1) % 2 == 0)
{
int y = (d_sqrt - 1) / 2;
if (x < y)
{
int[] temp = new int[y - x + 1];
for (int i = x; i <= y; i++)
{
temp[i - x] = i;
}
list.add(temp);
}
}
}
return list.toArray(new int[list.size()][]);
}
}
双指针
我们用两个指针 l 和 r 表示当前枚举到的以 l 为起点到 r 的区间,sum 表示 [l,r]的区间和,由求和公式可 O(1)
起始 l=1,r=2。
一共有三种情况:
- 如果 sum<target 则说明指针 r 还可以向右移动使得 sum 增大,此时指针 r 向右移动,即 r+=1
- 如果 sum>target 则说明以 l 为起点不存在一个 r 使得 sum=target ,此时要枚举下一个起点,指针 l 向右移动,即l+=1
- 如果 sum==target 则说明我们找到了以 l 为起点得合法解 [l,r] ,我们需要将 [l,r]
的序列放进temp数组并添加进list,且我们知道以 l 为起点的合法解最多只有一个,所以需要枚举下一个起点,指针 l 向右移动,即
l+=1
终止条件即为 l>=r 的时候,这种情况的发生指针 r 移动到了
的位置,导致 l<r 的时候区间和始终大于 target 。
此方法其实是对方法一的优化,因为方法一是没有考虑区间与区间的信息可以复用,只是单纯的枚举起点,然后从起点开始累加,而该方法就是考虑到了如果已知 [l,r] 的区间和等于 target ,那么枚举下一个起点的时候,区间 [l+1,r] 的和必然小于 target ,我们就不需要再从 l+1 再开始重复枚举,而是从 r+1 开始枚举,充分的利用了已知的信息来优化时间复杂度。
class Solution {
public int[][] findContinuousSequence(int target)
{
List<int[]> list = new ArrayList<>();
for (int l = 1, r = 2; l < r; ) //更新放在循环内部
{
int sum = (l + r) * (r - l + 1) / 2;
if (sum == target)
{
int[] temp = new int[r - l + 1];
for (int i = l; i <= r; i++)
{
temp[i - l] = i;
}
list.add(temp);
l++;
} else if (sum < target)
{
r++;
} else
{
l++;
}
}
return list.toArray(new int[list.size()][]);
}
}