剑指offer 学习笔记 和为s的数字
面试题57:和为s的数字。
1.输入一个递增的数组和一个数字s,在数组中查找两个数,使它们的和正好是s,输出一对这样的数即可。
即使是不好的直观解法,想到后也要告诉面试官,这样显得思维敏捷,如一种时间复杂度为O(n²)的解法,固定数组中的一个数字,然后依次判断数组中其余n-1个数字与它的和是否为s。
我们可以令两个指针分别指向数组中的首元素和尾元素,然后把这两个元素相加,如果和大于s,那么将后边的指针向前移动一个位置,因为数组是递增的,这样可以使两个指针指向元素的和更小,而如果和小于s,就将前面的指针向后移动一个位置:
#include <iostream>
using namespace std;
void FindNumsWithSum(int* nums, int length, int s) {
if (nums == nullptr || length < 2) {
cout << "输入有误。" << endl;
}
int ahead = 0, behind = length - 1;
while (ahead < behind) {
int sum = nums[ahead] + nums[behind];
if (sum == s) {
cout << nums[ahead] << "+" << nums[behind] << "=" << s << endl;
return;
} else if (sum > s) {
--behind;
} else {
++ahead;
}
}
cout << "没有两个数的和为" << s << endl;
}
int main() {
int nums[] = { 0,1,3,4,8,9,11,14,15 };
FindNumsWithSum(nums, sizeof(nums) / sizeof(*nums), 13);
}
2.输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数)。如,输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以打印出3个连续序列。
我们可以用两个数big和small表示序列的最大值与最小值,并将这两个数初始化为2和1,之后计算这两个数表示的序列的和,如和小于s,那么令big加1以容纳更多的数,如和大于s,那么令small加1。由于至少序列含两个数字,因此small要小于(s+1)/2:
#include <iostream>
using namespace std;
void PrintContinuousSequence(int small, int big) {
for (int i = small; i <= big; ++i) {
cout << i << " ";
}
cout << endl;
}
void FindContinuousSequence(int sum) {
if (sum < 3) {
return;
}
int small = 1, big = 2, middle = (1 + sum) >> 1, currSum = small + big;
while (small < middle) {
if (currSum == sum) {
PrintContinuousSequence(small, big);
}
while (currSum > sum && small < middle) {
currSum -= small;
++small;
if (currSum == sum) {
PrintContinuousSequence(small, big);
}
}
++big; // 在以上循环结束时,currSum小于等于sum,如小于,则应递增big,若等于,则已经输出此序列,应该递增big继续往后找下个序列
currSum += big;
}
}
int main() {
FindContinuousSequence(15);
}