剑指offer57-II.和为s的连续正数序列

看完题脑子里闪过了暴力法,就是从1开始往后累加,直到累加和等于或大于target,如果等于就放进数组,如果大于就从2开始加,但是这种想法只是闪过一下,因为我觉得加上填充数组需要3层循环肯定会超时,于是就直接想能不能有其他方法。

盯着15这个示例的答案看了一会我就想到了,15的答案里面有3个的有5个的,所以我可以去寻找target的因数,这个因数就是其中一个答案的中间那个数,target除以这个因数你就可以知道有多少个数,比如3是15的因数,15是3的5倍,就可把3放在5个相邻的自然数中间这样就出来了,但是这样只能判断出包含奇数个自然数的答案,无法判断偶数个自然数的答案,比如15的7和8那个答案就判断不出,然后我又盯着这两个案例看,我发现如果target除以这个偶数n余数是n/2就可以,比如9%2=2/2;于是9看可以分成4和5,15%2=2/2,所以15可以分成7和8,10%4=4/2,所以10可以分成1,2,3,4,5;8%2!=2/2,所以8不能分成2个连续自然数。

所以可以写1层循环,i表示target能分成的自然数的个数,因为个数要大于2,所以i从target/2取到2,首先判断i是奇数还是偶数,如果是奇数,看看是不是target的因数,如果是因数就可以取;如果是偶数,看看target%i是不是等于i/2,如果是就可以取。

那么该如何取呢?直接用target除以i,得到的数是平均数。平均数减去(i-1)/2,得到的数是第1个数,判断一下第一个数是不是大于0,小于0就跳过进入下一次循环,大于0的话就可以从第一个数开始取连续的i个数作为答案放进二维数组,以下是我的代码。

class Solution {
    public int[][] findContinuousSequence(int target) {
         int[][] res = new int[100][target/2];
         int index = 0;
         for(int i =target/2;i>1;i--){
             if(i % 2 == 1){
                 if(target % i == 0){
                      int a = target / i;
                      int begin = a-(i-1)/2;
                      if(begin<1)continue;
                      int[] b = new int[i];
                      for(int j=0;j<i;j++){
                          b[j] = (a-(i-1)/2)+j;
                      }
                      res[index++] = b;
                      //index++;
                 }
             }else{
               if(target % i == i/2){
                    double aver = target / i;
                    int[] c = new int[i];
                    double begin = aver-(i-1)/2;
                    if(begin<1)continue;
                    for(int k =0;k<i;k++){
                        c[k] = (int)begin + k;
                    } 
                    res[index++] = c;
                    //index++;
               }
             }
         }
         int[][] result = new int[index][target/2];
         for(int i =0;i<index;i++){
             result[i] = res[i];
         }
         return result;
    }
}

 写完之后看了一下题解,没想到题解第一个就用了暴力法,真打脸,

class Solution {
    public int[][] findContinuousSequence(int target) {
        List<int[]> vec = new ArrayList<int[]>();
        int sum = 0, limit = (target - 1) / 2; // (target - 1) / 2 等效于 target / 2 下取整
        for (int i = 1; i <= limit; ++i) {
            for (int j = i;; ++j) {
                sum += j;
                if (sum > target) {
                    sum = 0;
                    break;
                } else if (sum == target) {
                    int[] res = new int[j - i + 1];
                    for (int k = i; k <= j; ++k) {
                        res[k - i] = k;
                    }
                    vec.add(res);
                    sum = 0;
                    break;
                }
            }
        }
        return vec.toArray(new int[vec.size()][]);
    }
}

提解第二种是用的数学方法,他直接算出第一个数x最后一个数y和taget的数学关系,然后从1到target/2取遍历x,每遍历一个x都通过求根公式算出一个y,看看这个y是否合理,合理就取。

class Solution {
    public int[][] findContinuousSequence(int target) {
        List<int[]> vec = new ArrayList<int[]>();
        int sum = 0, limit = (target - 1) / 2; // (target - 1) / 2 等效于 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()][]);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荔枝味啊~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值