#数据结构与算法学习笔记#剑指Offer39:和为S的连续正数序列 + 序列规律/动态规划 + 测试用例(Java、C/C++)

234 篇文章 1 订阅
80 篇文章 0 订阅

2018.11.7     《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门​​​​​​​

这道题也是一道效率题,有两种比较技巧的做法。

方法一:序列规律

题目隐含条件为该数本身不算一个结果序列,那满足要求的序列一共有3种:1.两个数组成的序列,2.多个数组成的序列(奇数个数),3.多个数组成的序列(偶数个数)。

两个数组成的序列:很容易知道满足条件,简单地除2即可得到序列的前一个数。例如51由序列{25,26}加和而成。

多个数组成的序列(奇数个数):奇数个数的中位数必定是S的一个因数,奇数的个数为S的另一个因数,只需要在计算过程中保证序列最小值>0不超限即可。例如51的其中一个因数是17,另一个因数是3,那么以17为中心个数为3的的数列{16,17,18}就是其中一个满足条件的数列。而因数3作为中心值长度为17的序列最小值必然小于0,因此无法构成序列。

多个数组成的序列(偶数个数):偶数个数的序列情况与奇数个数的情况相似,最中心两个数的和为S其中一个因数的2倍。序列的元素个数为S的另一个因数。这里需要注意的是两个因子均为偶数的数S不能构成满足条件的序列。例如18的两个因数分别是9和2,那么{3,4,5,6}是一个满足的序列。

最后只需要按题目要求对满足要求的序列进行排序即可。

方法二:动态规划

从基础序列{1,2}开始,1为small,2为big,sum为3。若sum<S,big前进;若sum>S,small前进。每当sum==S的时候保存序列并使big前进,当small前进到S/2或者big前进到S/2+1停止。

例如对于S=9来说,初始序列{1,2},sum=3。

3<9,big前进,序列{1,2,3},sum=6;

6<9,big前进,序列{1,2,3,4},sum=10;

10>9,small前进,序列{2,3,4},sum=9,保存序列;

big前进,序列{2,3,4,5},sum=14;

14>9,small前进,序列{3,4,5},sum=12;

12>9,small前进,序列{4,5},sum=9,保存序列;

small=9/2,算法停止。


题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

输出描述:

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。

Java实现(方法一):

/**
 * 
 * @author ChopinXBP
 * 输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。
 *
 */

import java.util.ArrayList;
import java.util.Collections;

public class FindContinuousSequence_39 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ArrayList<ArrayList<Integer>> result = FindContinuousSequence(3);
		for(int i = 0; i < result.size(); i++){
			for(int j = 0; j < result.get(i).size(); j++){
				System.out.print(result.get(i).get(j) + " ");
			}
			System.out.println("");
		}
	}
	
    public static ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
    	if(sum <= 0)return null;
    	ArrayList<ArrayList<Integer>> result = new ArrayList<>();
    	if(sum <= 2)return result;
    	
    	//长度为2个数的序列(只有奇数有)
    	if((sum & 0x01) == 1){
    		ArrayList<Integer> list = new ArrayList<>();
    		list.add(sum / 2);
    		list.add(sum / 2 + 1);
    		result.add(list);
    	}
    	
    	//长度大于2个数的序列
        ArrayList<Integer> factor = new ArrayList<>();
        for(int i = 1; i < sum; i++){
        	if(sum % i == 0){
        		factor.add(i);
        	}
        }       
        for(int i = 0; i < factor.size(); i++){
        	int curfactor = factor.get(i);
        	int otherfactor = sum / curfactor;
        	//另一个因子为偶数
        	if((otherfactor & 0x01) == 0){
        		//两个因子均为偶数不可能构成序列
        		if((curfactor & 0x01) == 0) continue;
        		//以和为curfactor的两点为中心的序列长度不会超过正数界
        		int length = otherfactor / 2 - 1;
        		int center = curfactor / 2;
        		if(center > length){
        			ArrayList<Integer> list = new ArrayList<>();
        			for(int j = center - otherfactor + 1; j <= center + otherfactor; j++){
        				list.add(j);
        			}
        			result.add(list);
        		}
        	}
        	//另一个因子为奇数
        	else{
        		//以curfactor为中心点的序列长度不会超过正数界
        		int length = otherfactor / 2;
        		if(curfactor > length){
        			ArrayList<Integer> list = new ArrayList<>();
        			for(int j = curfactor - length; j <= curfactor + length; j++){
        				list.add(j);
        			}
        			result.add(list);
        		}
        	}       	
        }
        
        Collections.sort(result, (ArrayList<Integer> list1, ArrayList<Integer> list2) -> Integer.compare(list1.get(0), list2.get(0)));
        return result;
    }

}

C++实现(方法二):

#include "stdafx.h"

void PrintContinuousSequence(int small, int big);

void FindContinuousSequence(int sum)
{
    if(sum < 3)
        return;

    int small = 1; 
    int big = 2;
    int middle = (1 + sum) / 2;
    int curSum = small + big;

    while(small < middle)
    {
        if(curSum == sum)
            PrintContinuousSequence(small, big);

        while(curSum > sum && small < middle)
        {
            curSum -= small;
            small ++;

            if(curSum == sum)
                PrintContinuousSequence(small, big);
        }

        big ++;
        curSum += big;
    }
}

void PrintContinuousSequence(int small, int big)
{
    for(int i = small; i <= big; ++ i)
        printf("%d ", i);

    printf("\n");
}

测试代码:

// ====================测试代码====================
void Test(char* testName, int sum)
{
    if(testName != NULL)
        printf("%s for %d begins: \n", testName, sum);

    FindContinuousSequence(sum);
}

int _tmain(int argc, _TCHAR* argv[])
{
    Test("test1", 1);
    Test("test2", 3);
    Test("test3", 4);
    Test("test4", 9);
    Test("test5", 15);
    Test("test6", 100);

    return 0;
}

#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值