剑指 Offer 57 - II. 和为s的连续正数序列
题目:
思路:
【1】暴力的双指针循环处理的方式:基于题目限制,至少含有两个数,众所周知,x = x/2 + x/2,而在编程中会出现小数被遗弃的情况,所以当x=15时,一半是7,而7+8=15,故应该将边界值调大一些 limit = target/2 + 1; 然后基于数学公式计算多个有序的数的和有 (right + left) * (right - left + 1) / 2; 那么如果当数值小的时候右边界应该右移扩大,而数值过大的时候左边界应该缩小,刚好等于的话便将数值取出。
【2】枚举 + 数学优化:本身也是基于求和公式,推出 y^2+y−x^2+x−2×target=0。
关于 y 的一元二次方程,其中 a=1,b=1,c=−x^2+x−2×target (这里是将不是y的系数都提取出来,当做常量)
如:a*y^2+b*y+c =0
直接套用求根公式即可 O(1) 解得 y ,判断是否整数解需要满足两个条件:
1)判别式 b^2-4ac 开根需要为整数
2)最后的求根公式的分子需要为偶数,因为分母为 2
代码展示:
枚举 + 数学优化的方式:
//时间5 ms击败19.39% //内存39.6 MB击败49.16% //时间复杂度:O(n) 。 //空间复杂度:O(1) ,除了答案数组只需要常数的空间存放若干变量。 class Solution { public int[][] findContinuousSequence(int target) { List<int[]> vec = new ArrayList<int[]>(); int sum = 0, limit = (target) / 2; for (int x = 1; x <= limit; ++x) { long delta = 1 - 4 * (x - (long) x * x - 2 * target); if (delta < 0) { continue; } int delta_sqrt = (int) Math.sqrt(delta + 0.5); if ((long) delta_sqrt * delta_sqrt == delta && (delta_sqrt - 1) % 2 == 0) { int y = (-1 + delta_sqrt) / 2; // 另一个解(-1-delta_sqrt)/2必然小于0,不用考虑 if (x < y) { int[] res = new int[y - x + 1]; for (int i = x; i <= y; ++i) { res[i - x] = i; } vec.add(res); } } } return vec.toArray(new int[vec.size()][]); } }
暴力的双指针循环处理的方式:
//时间3 ms击败48.9% //内存39.4 MB击败76.82% //时间复杂度:由于两个指针移动均单调不减,且最多移动 target/2+1 次,所以时间复杂度为 O(target),即 O(n) 。 //空间复杂度:O(1) ,除了答案数组只需要常数的空间存放若干变量。 class Solution { public int[][] findContinuousSequence(int target) { int left = 1, right = 2; int limit = target/2 + 1; int sum; List<int[]> result = new ArrayList<int[]>(); while (right <= limit){ sum = (right + left) * (right - left + 1) / 2; if (sum == target){ int count = right - left + 1; int[] res = new int[count]; for (int i = left; i <= right; i++) { res[i - left] = i; } result.add(res); left++; }else if (sum < target){ right++; }else { left++; } } return result.toArray(new int[result.size()][]); //也可以换成下面的方式: // int ans[][] = new int[result.size()][]; // int n=0; // for(int[] item : result){ // ans[n++] = item; // } // return ans; } }