剑指Offer+第41题+和为s的两个数字+和为s的连续正数序列+java

本文介绍了一种高效算法,用于在已排序数组中查找两个数,使其和等于给定值,以及找出所有连续正数序列,其和等于指定正数。通过双指针技巧,算法能在O(n)时间内解决问题,避免了传统O(n^2)的复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目一:输入一个递增排序的数组和一个数字s,在数组中找两个数,使得他们的和刚好是s.如果有多对数字的和等于s,则输出任意一对即可。例如,输入数组{1,2,4,7,11,15}和数字15,则输出4和11(4+11=15)。
算法分析:
         首先,我们会想到在数组中固定一个数字,再一次判断数组中其余n-1个数字与它的和是不是等于S。不过这个方法的时间复杂度为O(n^2),会不会有更快的方法?
         接着我们提出时间更快的算法,我们现在数组中选择两个数字,如果它们的和等于输入的S,我们就找到了要找的两个数字。如果小于S呢?我们希望两个数字的和再大一点。由于数组已经排序好了,我们可以考虑选择较小的数字后面的数字,因为排在后面的数字要大一些,那么两个数字的和也要大一些,就有可能等于输入的数字S了。同样,当两个数字的和大于输入的数字的时候,我么可以选择较大数字前面的数字,因为排在数组前面的数字要小一些。
我们以数组{1,2,4,7,11,15}及期待的和为15为例详细分析一下这个过程。首先定义两个指针,第一个指针指向数组的第一个数字1,第二个指针指向数组的最后一个数字15.这两个数字的和为16大于15,因此我们把第二个指针向前移动一个数字,让它指向11.这个时候两个数字1与11的和为12,小于15,接下来我们把第一个指针向后移动一个数字指向2.此时两个数字2与11的和为13,还是小于15.我们再一次向后移动第一个指针,让它指向数字4.数字4与11的和是15,正是我们期盼的结果。
 

题目二:输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数)。例如,输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以打印出三个连续序列:1,2,3,4,5;4,5,6;7,8.
算法分析:
         有了前面的经验,我们也考虑用两个树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,此时序列为{ I, 2,坷。由于序列的和是6,仍然小于9,我们接下来再增加big 变成4,介于small 和big 之间的序列也随之变成{ l, 2, 3, 4}。由于列的和10 大于9,我们要删去去序列中的一些数字, 于是我们增加small 变成2,此时得到的序列是{2, 3, 4}, 序列的和E好是9。我们找到了第一个和为9 的连续序列,把它打印出来。接下来我们再增加big,重复前面的过程,可以找到第二个和为9 的连续序列{4,5}。

 

代码:

public class Offer41 {

	//问题1
	public boolean findNumbersWithSum(int [] data, int sum){
		boolean found = false;
		if(data == null || data.length<=0){
			System.out.println("invalid input");
			return found;
		}
		
		int left = 0;
		int right = data.length-1;
		//int num1 = 0;
		//int num2 = 0;
		while(left<right){
			int curSum = data[left]+data[right];
			if(curSum == sum){
				//num1 = data[left];
				//num2 = data[right];
				System.out.println(data[left]+" + "+data[right]+" = "+ sum);
				found = true;
				break;
			}else if(curSum > sum)
				right--;
			else
				left++;
		}
		//System.out.print(num1);
		//System.out.print(num2);
		if(!found)
			System.out.println("数组中没有找到和为"+sum+"的两个数");
		return found;
	}
	
	//问题2
	public void findContinuousSequence(int sum){
		boolean found = false;
		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)
				printContinusSequence(small, big);
			while(small < big && curSum > sum){
				curSum -= small;
				small++;
				
				if(curSum == sum)
					printContinusSequence(small, big);
			}
			
			big++;
			curSum += big;
		}
	}
	
	public void printContinusSequence(int small, int big) {
		for(int i =small;i<= big;i++)
			System.out.print(i+"  ");
		System.out.println();
	}

	public static void main(String[] args) {
		
		Offer41 of41 = new Offer41();
		/*
		//功能测试,1,数组中存在和为S的两个数
		int [] data = {1,2,4,7,11,15};
		int sum = 15;
		of41.findNumbersWithSum(data, sum);
		
		//功能测试,2,数组中不存在和为s的两个数
		int [] data2 = {1,2,4,7,15,17};
		int sum2 = 15;
		of41.findNumbersWithSum(data2, sum2);
		
		//特殊输入测试,3,表示数组的指针为NULL
		int [] data3 = null;
		int sum3 = 15;
		of41.findNumbersWithSum(data3, sum3);
		*/
		
		//功能测试 ,1,存在和为s 的连续序列,如9
		int sum1 = 9;
		of41.findContinuousSequence(sum1);
		System.out.println();
		
		//功能测试 ,2,存在和为s 的连续序列,如100
		int sum2 = 100;
		of41.findContinuousSequence(sum2);
		System.out.println();
		
		//功能测试 ,3,不存在和为s 的连续序列,如4
		int sum3 = 4;
		of41.findContinuousSequence(sum3);
		System.out.println();
		
		//功能测试 ,4,不存在和为s 的连续序列,如0
		int sum4 = 0;
		of41.findContinuousSequence(sum4);
		System.out.println();
		
		//边界值测试,5,连续序列的最小和为3
		int sum5 = 3;
		of41.findContinuousSequence(sum5);
		System.out.println();
		
	}
}

运行结果(问题1):

4 + 11 = 15
数组中没有找到和为15的两个数
invalid input

运行结果(问题2):

2  3  4  
4  5  

9  10  11  12  13  14  15  16  
18  19  20  21  22  



1  2  
3  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值