题目一:输入一个递增排序的数组和一个数字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