提示
建议先看程序逻辑,再看核心思想会更容易理解。
核心思想
核心思想还是利用二分查找,不过中间值不是通过下标确定的,而是通过数据计算出的中间值确定的。
假设给定的整数sum = 6
,给定的有N个元素的数组 arr = { 1,4,4,7 }
,从 arr
中任取两个元素(可以相同)它们的和所构成的一个集合,设为数组 c = { 2,5,8,11,14 }
,在数组 c
中进行查找,查找过程中数组 c
的变化如下图:
可以证明每次查找的中间值等于数组 arr
中的最大值与最小值之和,当然每次查找过后,数组 arr
和数组 c
所代表的数组会发生逻辑上的变化,现在这里先不讨论,具体在后面,先以上面第一次查找的过程说明,第一次查找时数组 c
中的最大值为数组 arr
中最大值的两倍,数组 c
中的最小值为数组 arr
中最小值的两倍,所以数组 c
中数据的中间值为 arr
中最大值与最小值的和。
现在来说明每次查找后,数组 arr
的变化和数组 c
的变化,由上面的证明可知,无需列举出数组 c
,通过数组 arr
就可以确定数组 c
的中间值,并且数组 c
中中间值左边的数包括中间值是由数组 arr
最左端的数与数组 arr
中各个元素的和,同样的,数组 c
中中间值右边的数包括中间值是由数组 arr
最右端的数与数组 arr
中各个元素的和,因此,删除数组 c
中间值左边的数就相当于删除数组 arr
中最左端的数,删除数组 c
中间值右边的数就相当于删除数组 arr
中最右端的数。
查找过程中数组 arr
的变化,如下图:
还需要确定a,b
,这其实就是每次在判断中间值时,进行记录。
程序逻辑
设定两个指针 left, right
,分别指向数组开始和结尾,即 left
指向最小值,right
指向最大值; 计算 arr[left]+arr[right]
的值,与 sum
比较,并与上一次记录的差值进行比较,如果这一次的差值小于上一次的差值则记录新的差值 diff
和 a, b
,若和小于 sum
,则 left++
,若和大于 sum
,则 right--
,若和等于 sum
,结束。
代码实现
#include <stdio.h>
#include <math.h>
int main()
{
int arr[] = { 1,4,4,7 };
int sum = 6;
int left = 0;
int right = sizeof(arr) / sizeof(arr[0]) - 1;
int a = arr[left]; //这里对a,b的初始化是为了防止,数组中只有一个值,
int b = arr[right]; //当然也可以改变下面对于差值的判断条件,改为大于等于
int aim = 0;
int diff = abs(arr[left] + arr[right] - sum);
while (left <= right)
{
aim = arr[left] + arr[right];
//如果所找的数,不在给定数组元素所能够得到的和中,
//就需要记下与所找数距离最近的和的a,b的值。
if (diff > abs(aim - sum))
{
diff = abs(aim - sum);
a = arr[left];
b = arr[right];
}
//筛除数据
if (sum > aim)
{
left++;
}
else if (sum < aim)
{
right--;
}
else
{
a = arr[left];
b = arr[right];
break;
}
}
printf("%d %d", a, b);
return 0;
}
= arr[right];
break;
}
}
printf("%d %d", a, b);
return 0;
}