您的位置:首页 > 职场人生

《剑指Offer》学习笔记--面试题41:和为s的两个数字VS和为s的连续正数序列

2015-05-14 10:15 495 查看
题目一:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好时s。如果有多对数字的和等于s,输出仍以一对即可。

例如输入数组{1,2,4,7,11,15}和数字15,因此输出4和11.

面试的时候,很重要的一点是应聘者要表现出很快的反应能力。只要想到一个方法,应聘者就可以马上告诉面试官,即使这个方法不一定是最好的。比如这个问题,很多人都能立即想到O(n^2)的方法,也就是现在数组中固定一个数字,再依次判断数组中其余的n-1个数字与它的和是不是等于s。面试官会告诉我们这不是最好的办法。不过这没有关系,至少面试官知道我们思维还是比较敏捷的。

接着我们寻找更好的算法。我们现在数组中选择两个数字,如果它们的和等于输入的s,我们就找到了要找的两个数字。如果和小于s呢?我们希望两个数字的和再大一点。由于数组已经排好序了,我们可以考虑选择较小的数字后面的数字。因为排在后面的数字要大一些,那么两个数字的和也要大一些,就有可能等于输入的s了。同样,当两个数字的和大于输入的数字的时候,我们可以选择较大的前面的数字,因为排在数组前面的数字要小一些。

这次面试官会首肯我们的思路,于是就可以动手写代码了:

bool FindNumbersWithSum(int data[], int length, int sum, int *num1, int num2)
{
bool found = false;
if(length < 1 || num1 == NULL || num2 == NULL)
return found;

int ahead = length - 1;
int behind = 0;

while(ahead > behind){
long long curSum = data[ahead] + data[behind];

if(curSum == sum){
*num1 = data[ahead];
*num2 = data[behind];
found = true;
break;
}
else if(curSum > sum)
behind--;
else
ahead ++;
}

return found;
}
在上述代码中,ahead为较小的数字的下标,behind为较大的数字的下标。由于数组是排序的,因此较小的数字一定位于较大数字的前面,这就是while循环继续的条件是ahead>behind的原因。代码中只有一个wihle循环从两端向中间扫描数组,因此这种算法的时间复杂度是O(n).

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

形成了思路后,我们就可以开始写代码了:

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 < big){
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"m i);
printf("\n");
}
在前面的代码中,求连续序列的和应用了一个小技巧。通常我们可以用循环求一个序列的和,但考虑每一次操作之后的序列和操作之前的序列相比大部分数字都是一样的,只是增加或者减少了一个数字,因此我们可以再前一个序列的和的基础上求操作之后的序列的和。这样可以减少很多不必要的运算,从而提高了代码的效率。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐