剑指Offer——面试题41:和为s的两个数字 VS 和为s的连续正数序列

题目1:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得他们的和正好是s。如果有多对数字的和等于s,全部输出

题目2:输入一个正数s,打印所有的和为s的连续正数序列(至少含有两个数)。例如输入15,由于1+2+3+4+5 = 4+5+6 = 7+8 = 15,所以打印三个连续序列,即1-5, 4-6, 7-8。

思路:两个题目的特殊之处,都用黑体标出来了。另外题目一的要求,我略作改动,与原书不同。两个题总体思路都是一致的,两个指针一前一后,根据当前指向或当前范围确定和,在和输入的s比较。如果一致,就输出;如果不一致,就动态调整两个指针。

第一个题目中,前边的指针指向数组最前位置,后边的指针指向数组最后位置,设cursum为两个指针指向的数字之和,cursum>s时,前一个指针后移,否则后一个指针前移。当cursum==s时,输出这两个数字。另外,我们假设输入的数组中不出现重复的数字,那么在输出这两个符合条件的数字后,需要同时移动前后两个指针。按照这个规则移动指针,直到两个指针相遇。这个题目还算简单,这样基本就OK了。

第二个题目中,由于是需要打印连续的正整数序列,所以不需要单独开空间存放数组,直接使用两个int类型的变量即可。两个指针之间的数字之和为cursum,当cursum>s时,前一个指针head移动(只允许后移),否则后一个指针rear移动(也是后移)。当cursum==s时,输出两个指针之间的数字,并同时移动两个指针。

这里还有一点问题,就是什么时候需要停下来。根据题目要求,至少含有两个数的时候,才称为序列。那么两个连续的数字,其和只可能是奇数,且一个是(s+1)/2,另一个是(s-1)/2。考虑到这些以后,我们只需要让rear不超出(s+1)/2即可。

之后的另一个问题就是,怎么保证head和rear不重叠,即保证序列最少包含两个数。可以发现,当两个指针相邻时,如果还没有达到(s+1)/2,那么cursum必然会小于s,这时rear一定会后移。这样就可以保证head在(s+1)/2的范围之内,永远不会与rear重叠。

下面是第一题的代码:

void PrintSumOfTwoNum(const int data[], const int length, const int sum){
	if(length < 2) return;

	int head = 0, rear = length - 1;
	long cursum = 0L;
	while(head < rear){
		cursum = data[head] + data[rear];

		if(cursum == sum)
			cout << data[head++] << ", " << data[rear--] << "." << endl;
			//Assume that each number appears only once.
		else{
			if(cursum > sum)
				--rear;
			else
				++head;
		}
	}//End while
}//End func

  下面是第二题的代码:

long SumTo(int head, int rear){
	long cursum = 0L;
	while(head <= rear)
		cursum += head++;
	return cursum;
}

void PrintSumSequence(const int sum){
	if(sum <= 2) return;

	int head = 1, rear = 2;
	long cursum = 0L;
	while(rear <= (sum / 2) + 1){ //End at half of sum plus 1.
		cursum = SumTo(head, rear);
		if(cursum == sum){
			for(int i = head; i < rear; ++i)
				cout << i << ", ";
			cout << i << "." << endl;
			++head;
			++rear;
		}
		else{
			if(cursum < sum)
				++rear;
			else				
				++head;
		}
	}//End while
}//End func

  下面是测试代码:

#include <iostream.h>

const int L = 7;

int main(){
	int test[L] = {1, 2, 4, 7, 11, 13, 15};
	PrintSumOfTwoNum(test, L, 15);
	PrintSumSequence(15);

	return 0;
}

  上边没有讨论边界情况。另外测试用例还可以多改一改。都不赘述了。

原文地址:https://www.cnblogs.com/superpig0501/p/4075049.html