ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

538,剑指 Offer-和为s的连续正数序列

2021-06-15 07:07:40  阅读:172  来源: 互联网

标签:arr 窗口 target Offer int res left 正数 538


You can know everything in the world, but the only way you're findin' out that one is by givin' it a shot. 

你可以了解世间万物,但追根溯源的唯一途径便是亲身尝试。

问题描述

输入一个正整数target,输出所有和为target的连续正整数序列(至少含有两个数)。

 

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

 

示例 1:

输入:target = 9

输出:[[2,3,4],[4,5]]

示例 2:

输入:target = 15

输出:[[1,2,3,4,5],[4,5,6],[7,8]]

 

限制:

  • 1 <= target <= 10^5

 

滑动窗口解决

滑动窗口,这里也叫双指针,因为题中要求的是正整数,连续的,并且至少含有两个数。所以我们使用两个指针,一个left指向1,一个right指向2,他们分别表示窗口的左边界和右边界。然后计算窗口内元素的和。

 

  • 如果窗口内的值大于target,说明窗口大了,left往右移一步。

  • 如果窗口内的值小于target,说明窗口小了,right往右移一步。

  • 如果窗口内的值等于target,说明找到了一组满足条件的序列,把它加入到列表中

 

我们以示例1为例来看下视频演示

因为至少有两个数,所以窗口的左边界left <= target / 2,题中是把找到的序列添加到列表list中,最后在转化为二维数组,来看下代码

 1public int[][] findContinuousSequence(int target) {
2    int left = 1; // 滑动窗口的左边界
3    int right = 2; // 滑动窗口的右边界
4    int sum = left + right; // 滑动窗口中数字的和
5    List<int[]> res = new ArrayList<>();
6    //窗口的左边是窗口内的最小数字,只能小于等于target / 2,
7    //因为题中要求的是至少含有两个数
8    while (left <= target / 2) {
9        if (sum < target) {
10            //如果窗口内的值比较小,右边界继续向右移动,
11            //来扩大窗口
12            sum += ++right;
13        } else if (sum > target) {
14            //如果窗口内的值比较大,左边边界往右移动,
15            //缩小窗口
16            sum -= left++;
17        } else {
18            //如果窗口内的值正好等于target,就把窗口内的值记录
19            //下来,然后窗口的左边和右边同时往右移一步
20            int[] arr = new int[right - left + 1];
21            for (int k = left; k <= right; k++) {
22                arr[k - left] = k;
23            }
24            res.add(arr);
25            //左边和右边同时往右移一位
26            sum -= left++;
27            sum += ++right;
28        }
29    }
30    //把结果转化为数组
31    return res.toArray(new int[res.size()][]);
32}

 

数学公式解决

我们假设有一组序列满足条件,其中序列的第一个数是a,他们分别是a,a+1,a+2,……,a+(n-1),总共有n项,根据求和公式我们可以得出S=n*a+n*(n-1)/2,而S其实就是target,我们简写成t,来研究一下这个公式

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

要想求a,我们可以通过循环枚举n的值,只有a是正整数的时候才满足条件,那么这个循环什么时候终止呢,其实很简单,当分子t-n*(n-1)/2小于等于0的时候就可以终止了。

 

因为题中说了最少要有2个数,所以n从2开始,来看下代码

 1public int[][] findContinuousSequence(int target) {
2    List<int[]> res = new ArrayList<>();
3    int n = 2;
4    //死循环
5    while (true) {
6        int total = target - n * (n - 1) / 2;
7        //当分子小于等于0的时候,退出循环
8        if (total <= 0)
9            break;
10        //如果首项是正整数,满足条件
11        if (total % n == 0) {
12            int[] arr = new int[n];
13            //找出首项的值
14            int startValue = total / n;
15            for (int k = 0; k < n; k++) {
16                arr[k] = startValue + k;
17            }
18            res.add(arr);
19        }
20        //继续找
21        n++;
22    }
23    //反转,比如当target等于9的时候,结果是
24    //[[4,5],[2,3,4]],但题中要求的是不同
25    // 序列按照首个数字从小到大排列,所以这里反转一下
26    Collections.reverse(res);
27    //把list转化为数组
28    return res.toArray(new int[res.size()][]);
29}

 

数学的另一种解决方式

我们来思考这样一个问题

 

  • 假如target是两个连续数字的和,那么这个序列的首项就是(target-1)/2。

  • 假如target是三个连续数字的和,那么这个序列的首项就是(target-1-2)/3。

  • 假如target是四个连续数字的和,那么这个序列的首项就是(target-1-2-3)/4。

  • ……

 

证明也很好证,我们随便找一个,假如target是四个连续的序列和,那么这四个数字就是

a,a+1,a+2,a+3

也就是

4*a+1+2+3=target

所以他们的首项

a=(target-1-2-3)/4。

 

搞懂了上面的原理代码就简单了,我们来看下

 1public int[][] findContinuousSequence(int target) {
2    List<int[]> res = new ArrayList<>();
3    //因为至少是两个数,所以target先减1
4    target--;
5    for (int n = 2; target > 0; n++) {
6        //找到了一组满足条件的序列
7        if (target % n == 0) {
8            int[] arr = new int[n];
9            //找出首项的值
10            int startValue = target / n;
11            for (int k = 0; k < n; k++) {
12                arr[k] = startValue + k;
13            }
14            res.add(arr);
15        }
16        target -= n;
17    }
18    Collections.reverse(res);
19    //把list转化为数组
20    return res.toArray(new int[res.size()][]);
21}

 

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

536,剑指 Offer-构建乘积数组

535,剑指 Offer-扑克牌中的顺子

533,剑指 Offer-最小的k个数

432,剑指 Offer-反转链表的3种方式

 

 

截止到目前我已经写了500多道算法题了,为了方便大家阅读,我把部分算法题整理成了pdf文档,目前有900多页,大家可以在公众号中回复关键字“pdf”即可获取下载链接。

 

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=你点的每个赞,我都认真当成了喜欢

标签:arr,窗口,target,Offer,int,res,left,正数,538
来源: https://blog.51cto.com/u_4774266/2902944

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有