查找最接近的元素(二分法)

一、题目大意
题目传送门
查找最接近的元素(分治法/二分查找):在一个非降序列中,查找与给定值最接近的元素。

[输入]

第一行包含一个整数n,为非降序列长度。

第二行包含n个整数,为非降序列各元素。

第三行包含一个整数m,为要询问的给定值个数。1 <= m <= 10000。

接下来m行,每行一个整数,为要询问最接近元素的给定值。所有给定值的大小均在0-1,000,000,000之间

[输出]

m行,每行一个整数,为最接近相应给定值的元素值,保持输入顺序。若有多个值满足条件,输出最小的一个

input:

3

2 5 8

2

10

5

output:

8

5

思路如下:

这一题就是用二分法求最接近所给m在 a[] 数组中的值,由于是最接近,我们需要对情况进行更细致的讨论,这里我会的有两种方式解这一题:
第一种是用递归的方式来写二分(但是注意这种方法在这一题是行不通的因为数据太大,递归层数太多,造成栈溢出,但是分类讨论的思想还是值的我们学习的);另外一种就是用while循环的方法写二分

递归题解

/*二分递归找最近的值的步骤:
    主要思想是:在不断细分区间的时候,通过不同的判断条件,去不断的选择符合题意的区间,当选择了某一个区间后,并对该区间进行分类讨论:讨论1.看min与边界的差值是否为1,如果是1,那么我们想要的结果就是在ar[mid]与ar[边界值]之间选择(通过看值两个数与所要找的数的差值,进行比较,还要注意两者均是可以选择的)

1.讨论要查找的值是否在所求区间的外边(那么最接近要查找的值就是边界值,所以接下来要对边界值进行讨论)
2.讨论 要查找的值位于做边界值与ar[mid] 之间
3.讨论 要查找的值位于ar[mid]与右边界值之间
4.讨论 要查找的值等与ar[mid]的情况
*/
#include
#include
int a[10005];//存放升序排列的一列数字
int b[10005];//存放要找的数字
int m;//输入要查找的数的个数
long long finder(int mx, int mn, int mid, int j)//
{//第一步区分查找的这个数是否在开区间内
	if (b[j] <= a[mn] || b[j] >= a[mx])//不在
	{
		//对内部进行细分,到底是比最小的还小,还是比最大的还大
		if (b[j] <= a[mn])  //比最小的还小
			printf("%d\n", a[mn]);
		else//比最大的还大
			printf("%d\n", a[mx]);
	}
	else//位于最小的数字和最大的数字之间
	{
		//根据mid来分,比mid大还是比mid小,还是和mid相等
		if (a[mn]<b[j] && b[j]<a[mid])//比mid小
		{
			if (mid - mn != 1)
			{
				mx = mid - 1;//将mx替换为mid
				mid = (mn + mx) / 2;//重新计算mid下标
				finder(mx, mn, mid, j);
			}
			else
			{
				if (a[mid] - b[j]>b[j] - a[mn])//比较距离
					printf("%d\n", a[mn]);
				else if (a[mid] - b[j]<b[j] - a[mn])
					printf("%d\n", a[mid]);
				else
					printf("%d %d\n", a[mn], a[mid]);
			}
 
		}
		else if (a[mid]<b[j] && b[j]<a[mx])//比mid大
		{
 
			if (mx - mid != 1)
			{
				mn = mid;//重新计算mn
				mid = (mn + mx) / 2;//重新计算mid下标
				finder(mx, mn, mid, j);
			}
			else
			{
				if (b[j] - a[mid]>a[mx] - b[j])
					printf("%d\n", a[mx]);
				else if (b[j] - a[mid]<a[mx] - b[j])
					printf("%d\n", a[mid]);
				else
					printf("%d %d\n", a[mid], a[mx]);
			}
		}
		else//和mid相等
			printf("%d\n", b[j]);
	}
	return 0;
}
int main()
{
	int j = 0;//用作函数循环的
	int n;//输入非降序序列的长度
	scanf("%d", &n);
	for (int i = 0; i<n; i++) //循环输入n个数字
		scanf("%d", &a[i]);
	scanf("%d", &m);    //输入要查询的数字个数
	for (int i = 0; i<m; i++) //循环输入要查找的数,存在b[i]中
		scanf("%d", &b[i]);
	int mx = n - 1, mn = 0;
	int mid = (mx + mn) / 2;
	for (j = 0; j<m; j++)//循环调用函数,把b[j]中存储的所有要找的数字找完
		finder(mx, mn, mid, j);
	
	return 0;
 
}

whille循环题解

#include
#include
#include
#include
using namespace std;
long long a[100001];
int n;
long long Binary_search(long long int b)
{
    //首先对是否在该区间以外进行讨论
    int l = 0;
    int r = n - 1;
    if(b <= a[l])
        return a[l];
    if(b >= a[r])
        return a[r];
    //在所求区间内进行二分讨论
    while(r - l > 1)        //while循环里面的r - l > 1 这个限定条件很重要,当r yu l 的差值为1 的时候就应该停止循环了,在分别ar[l] 与 ar[r] 两个值讨论看那个值更合适
    {
        int mid = (l + r) / 2;
        if(b >= a[mid])
            l = mid;	  //在这里 l 直接等于 mid ,否则会出错
        else
            r = mid;	  // 同理
    }
    long long int x = abs(a[l] - b);
    long long int y = abs(a[r] - b);
    return x <= y ? a[l] : a[r];
}

int main()
{
//    freopen("T.txt","r",stdin);
    int i,m;
    long long b;
    scanf("%d",&n);
    for(i=0;i<n;i++){
        scanf("%lld",&a[i]);
    }
    scanf("%d",&m);
    while(m){
        scanf("%lld",&b);
        printf("%lld\n",Binary_search(b));
        m--;
    }
    return 0;
}

你可能感兴趣的:(#,二/三分法)