接近sum问题最快的平均时间复杂度解法的核心思想

题目

提示

​ 建议先看程序逻辑,再看核心思想会更容易理解。

核心思想

​ 核心思想还是利用二分查找,不过中间值不是通过下标确定的,而是通过数据计算出的中间值确定的。

​ 假设给定的整数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 比较,并与上一次记录的差值进行比较,如果这一次的差值小于上一次的差值则记录新的差值 diffa, 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值