Day41:和为s的连续正数序列

时间:2022-07-24
本文章向大家介绍Day41:和为s的连续正数序列,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

剑指Offer_编程题——和为s的连续正数序列

题目描述:

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

输出描述

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

具体要求:

时间限制: C/C++ 1秒,其他语言2秒 空间限制: C/C++32M,其他语言64M

具体实现:

思路一:   直接用暴力法,其时间复杂度O(n^2),其思路较为直接,因此我们可以用python将其实现,具体实现如下:

import math
class Solution:
	def FindContinuousSequence(self, tsum):
		if tsum == 0 or tsum == 1:
			return []
		mid_ceil = int(math.ceil(float(tsum)/2))
		result_list = []
		for i in range(1, mid_ceil + 1, 1):
			temp_sum = 0
			for j in range(i, 0, -1):
				temp_sum += j
				if temp_sum == tsum:
					temp_list = [k for k in range(j, i + 1, 1)]
					result_list.append(temp_list)
		return result_list

代码效果图如图所示:

  本代码在牛客网中本来可以通过了,但是为了我们在本地的编译器(PyCharm)看一看到底是不是题目中运行的结果,得在代码后面加上main函数来执行代码,具体用Python实现如下:

if __name__ == "__main__":
	sol = Solution()
	print(sol.FindContinuousSequence(100))

代码编译如图所示:

思路二:   我们可以考虑用两个数small和big分别表示序列的最小值和最大值。首先把small初始化为1,big初始化为2。如果从small到 big的序列的和大于s,则可以从序列中去掉较小的值,也就是增大small的值。如果从small到big的序列的和小于s,则可以增大big,让这个序列包含更多的数字。因为这个序列至少要有两个数字,我们一直增加small到(1+s)/2 为止。   以求和为9的所有连续序列为例,我们先把small初始化为1,big初始化为2。此时介于small和big之间的序列是{1,2},序列的和为3,小于9,所以我们下一步要让序列包含更多的数字。我们把big增加1变成3,此时序列为{1,2,3}。由于序列的和是6,仍然小于9,我们接下来再增加big变成4,介于 small 和big之间的序列也随之变成{1,2,34}。由于序列的和10大于9,我们要删去序列中的一些数字,于是我们增加small变成2,此时得到的序列是{2,3,4},序列的和正好是9。我们找到了第一个和为9的连续序列,把它打印出来。接下来我们再增加big,重复前面的过程,可以找到第二个和为9的连续序列{4, 5} 。可以用下表总结整个过程。

  接下来我们分别用python将其实现。

class Solution:
	def FindContinuousSequence(self, tsum):
		if tsum == 0 or tsum == 1 or tsum == 2:
			return []
		result_list = []
		mid = int((tsum + 1) // 2)
		array = [i for i in range(0, mid+1)]
		small = 1
		big = 2
		temp_sum = array[small] + array[big]
		temp = [array[small], array[big]]
		while True:
			if temp_sum == tsum:
				temp_list = [t for t in temp]
				result_list.append(temp_list)
				if big < len(array) - 1:
					big += 1
					temp_sum += array[big]
					temp.append(array[big])
				else:
					return result_list
			if temp_sum > tsum:
				temp_sum -= array[small]
				temp.remove(array[small])
				small += 1
			if temp_sum < tsum:
				if big < len(array) - 1:
					big += 1
					temp_sum += array[big]
					temp.append(array[big])
				else:
					return result_list
		return result_list

代码效果图如图所示:

  代码在本地测试结果如下:

  在上述代码中,求连续序列的和应用了一个小技巧。通常我们可以用循环求一个连续序列的和,但考虑到每次操作之后的序列和操作之前的序列相比大部分数字都是一样的,只是增加或者减少了一个数字,因此我们可以在前一个序列的和的基础上求操作之后的序列的和。这样可以减少很多不必要的运算,从而提高代码的效率。并且时间复杂度为O(n)。 思路三:   理解题意,应该是一个二维的数组。 1、首先,定义两个变量min和max,分别指向序列的第一个元素和第二个元素; 因为是求连续数列的和,所以当max指向的数小于所求和sum的时候,分为以下三种情况:   (1)当连续数列的和num等于sum的时候,就需要把这个序列压到一维数组中去,紧接着压入到二维数组中,再改变min和max的值;   (2)当连续数列的和num小于sum的时候,此时需要改变max的值,使其变得更大一些,这样才能更加接近sum;    (3)当连续数列的和num大于sum的时候,此时需要改变min的值,使其变得更大一些,这样才能更加接近sum。   最后返回这个二维数组即可。具体用Java实现如下:

import java.util.ArrayList;
public class Solution{
	public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum){
		ArrayList<ArrayList<Integer>> array = new ArrayList<ArrayList<Integer>>();
		int min = 1;
		int max = 2;
		while(max < sum){
			int num = (max - min + 1) * (min + max) / 2;
			if(num == sum){
				ArrayList<Integer> array1 = new ArrayList<Integer>();
				for(int i = min; i <= max; i++){
					array1.add(i);
				}
				array.add(array1);
				min++;
				max++;
			}
			else if(num < sum)
				max++;
			else
				min++;
		}
			return array;
	}
}

代码效果图如图所示:

总结

   本道题通过穷举来考察我们对综合知识的应用,在本题中我么用了三种方法来解决此问题。刚开始我们用到的是最简单、最暴力的算法。但是,时间复杂度比较高,为O(n^2),接下来我们用到了两个序列small和big,通过一系列算法将其实现,发现时间复杂度只有O(n),在这种算法里通过一个具体的案例来体现其算法的计算过程。在这个算法里运用了一个小技巧来求和。最后就是通过二维数组来将其实现。因此,我们在做题的时候,应该多次尝试各种方法,扩展自己的思维,写出优质的代码。总之,我们要继续加油,争取早日找到工作,Good Luck!!!

参考文献

[1] Microstrong0305 [2] 墨羽lp