面试算法(四十一)和为s的两个数字VS和为s的连续正数序列
2014-07-18 14:17
218 查看
1、题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,输出任意一对即可。
例如输入数组{1,2,4,7,11,15}和数字15,由于4+11=15,因此输出4和11。
直观解法是先在数组中固定一个数字,再依次判断数组中其余的n-1个数字与它的和是不是等于s。
解法:
先在数组中选择两个数字,如果他们的和等于输入的s,就找到了符合要求的两个数字。如果和小于s呢,希望这两个数字的和再大一点。由于数组已经排好序了,我们可以考虑选择较小的数字后面的数字。当和大于s时同理。
1)以上面例子进行分析,首先定义两个指针,第一个指针指向数组的第一个(也是在最小的)数字1,第二个指针指向数组的最后一个(也是最大的)数字15。这两个数字的和16大于15,因此把第二个指针向前移动一个数字,让它指向11,此时两个数字的和12小于15。接下来把第一个指针向后移动一个数字指向2,此时和为13小于15。再一次向后移动第一个指针,让它指向数字4。此时和为15即为所求。
2、题目二:
输入一个正数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的值。若小于s则增大big。因为这个序列至少要有两个数字,我们一直增加small到(1+s)/2为止。
1)以求和为9的所有连续序列为例,初始化为{1,2},序列和为3小于9,把big增加1变为3,此时序列为{1,2,3},和为6扔小于9,再增加big变为4,序列变为{1,2,3,4}。由于序列和10大于9,要删去一些数字,于是增加small变为2,此时序列为{2,3,4},序列和刚好是9。接下来再增加big,重复前面的过程,可以找到第二个和为9的连续序列{4,5}。
例如输入数组{1,2,4,7,11,15}和数字15,由于4+11=15,因此输出4和11。
直观解法是先在数组中固定一个数字,再依次判断数组中其余的n-1个数字与它的和是不是等于s。
解法:
先在数组中选择两个数字,如果他们的和等于输入的s,就找到了符合要求的两个数字。如果和小于s呢,希望这两个数字的和再大一点。由于数组已经排好序了,我们可以考虑选择较小的数字后面的数字。当和大于s时同理。
1)以上面例子进行分析,首先定义两个指针,第一个指针指向数组的第一个(也是在最小的)数字1,第二个指针指向数组的最后一个(也是最大的)数字15。这两个数字的和16大于15,因此把第二个指针向前移动一个数字,让它指向11,此时两个数字的和12小于15。接下来把第一个指针向后移动一个数字指向2,此时和为13小于15。再一次向后移动第一个指针,让它指向数字4。此时和为15即为所求。
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[behind]; *num2 = data[ahead]; found = true; break; } else if(cueSum > sum) ahead--; else behind++; } return found; }上述代码中,ahead为较小的数字的下标,behind为较大的数字的下标。由于数组是排序的,因此较小的数字一定位于较大数字的前面,这就是while循环继续的条件是ahead>behind的原因。只有一个while循环从两段向中间扫描数组,因此时间复杂度是O(n)。
2、题目二:
输入一个正数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的值。若小于s则增大big。因为这个序列至少要有两个数字,我们一直增加small到(1+s)/2为止。
1)以求和为9的所有连续序列为例,初始化为{1,2},序列和为3小于9,把big增加1变为3,此时序列为{1,2,3},和为6扔小于9,再增加big变为4,序列变为{1,2,3,4}。由于序列和10大于9,要删去一些数字,于是增加small变为2,此时序列为{2,3,4},序列和刚好是9。接下来再增加big,重复前面的过程,可以找到第二个和为9的连续序列{4,5}。
bool 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"); }通常我们可以循环求一个连续序列的和,但考虑到每一次操作之后的序列和操作之前的序列相比大部分数字是一样的,只是增加或减少了一个数字,因此我们可以在前一个序列的和的基础上求操作之后的序列的和。这样减少了不必要的运算,提高了效率。
相关文章推荐
- 剑指offer:和为s的两个数字VS和为s的连续正数序列(java)
- 剑指offer 41 - 和为s的两个数字VS为s的连续正数序列
- 【剑指offer】6.3知识迁移能力——面试题41:和为s的两个数字VS和为s的连续正数序列
- 【剑指Offer学习】【面试题41:和为s 的两个数字vs 和为s 的连续正数序列】
- 面试题41:和为s的两个数字VS和为s的连续正数序列
- 面试题41和为S的两个数字VS和为s的连续正数序列
- 和为S的两个数字 VS 和为S的连续正数序列
- 和为S的两个数字VS和为S的连续正数序列
- 剑指Offer之 - 和为s的两个数字 VS 和为s的连续正数序列
- 剑指offer 面试题41—和为s的两个数字VS和为s的连续正数序列
- 和为S的两个数字VS和为s的连续正数序列
- 剑指Offer:和为s的两个数字VS和为s的连续正数序列
- 剑指Offer(Java版):和为s的两个数字VS和为s的连续正数序列
- 【剑指offer-Java版】41和为s的两个数字VS和为s的连续正数序列
- 和为s的两个数字 VS 和为s的连续正数序列
- 剑指offer面试题 和为s的两个数字 VS 和为s的连续正数序列
- 剑指offer41:和为s的两个数字VS和为s的连续正数序列
- 剑指Offer 41题 和为s的两个数字VS 何为S的连续正数序列 Java版
- 【面试题041】和为s的两个数字VS和为s的连续正数序列
- 面试题41:和为s的两个数字VS和为s的连续正数序列