【数据结构每日一题】线性表——三元组的最短距离

[数据结构习题]线性表——三元组的最短距离



👉知识点导航💎:【数据结构】线性表——顺序存储

👉[王道数据结构]习题导航💎: p a g e 19.14 page19.14 page19.14

本节为线性表的习题

在这里插入图片描述




题目描述:

在这里插入图片描述



🎇思路①:暴力法

🔱思路分析:

🍰如果不做其他要求,对于求解三个数组中的最短距离问题,我们最先会想到暴力枚举法进行计算,即不断更新最短距离,直到循环结束


💫这里可能会想到暴力+剪枝的方法,但 时间复杂度仍为 O ( n 3 ) O(n^3) O(n3)

在这里插入图片描述


🚀对于功能函数:

  1. 先定义一个 极大值 I N F ( 这里极大值设为 0 x 3 f 3 f 3 f ) INF (这里极大值设为0x3f3f3f) INF(这里极大值设为0x3f3f3f)

  2. 采用三重循环的方式,依次抽取三个数组中的元素

  3. 因为 d = ∣ b [ j ] − a [ i ] ∣ + ∣ c [ k ] − b [ j ] ∣ + ∣ a [ i ] − c [ k ] ∣ d = |b[j] - a[i]| + |c[k] - b[j]| + |a[i] - c[k]| d=b[j]a[i]+c[k]b[j]+a[i]c[k],所以,前两重循环之后,我们只能进行一次剪枝

    //剪枝
    if (abs(b[j] - a[i]) >= min) //如果这个距离已经大于当前最小值了,则结束此轮
        continue;
    

    因为有两个表达式与三个元素有关,所以当出现两个元素时,只能确定一个表达式,进行第三次循环后,再做出判断

  4. 不断更新最小值,并加入到三元组中,直到遍历结束


代码实现:

#define INF 0x3f3f3f

int Getmin(int a[], int b[], int c[], int l1,int l2, int l3,int * triple)
{
	int min = INF;

	for (int i = 0; i < l1; i++) {
		for (int j = 0; j < l2; j++) {
			//剪枝
			if (abs(b[j] - a[i]) >= min) //如果这个距离已经大于当前最小值了,则结束此轮
				continue;
			for (int k = 0; k < l3; k++) {
				int d = abs(b[j] - a[i]) + abs(c[k] - b[j]) + abs(a[i] - c[k]);
				if (d < min)
				{
					min = d;
					triple[0] = a[i]; triple[1] = b[j]; triple[2] = c[k];
				}
			}
		}
	}
	return min;
}

🚀完整代码实现:

#include<iostream>
#include<cmath>
#define INF 0x3f3f3f
using namespace std;

int Getmin(int a[], int b[], int c[], int l1,int l2, int l3,int * triple)
{
	int min = INF;

	for (int i = 0; i < l1; i++) {
		for (int j = 0; j < l2; j++) {
			//剪枝
			if (abs(b[j] - a[i]) >= min) //如果这个距离已经大于当前最小值了,则结束此轮
				continue;
			for (int k = 0; k < l3; k++) {
				int d = abs(b[j] - a[i]) + abs(c[k] - b[j]) + abs(a[i] - c[k]);
				if (d < min)
				{
					min = d;
					triple[0] = a[i]; triple[1] = b[j]; triple[2] = c[k];
				}
			}
		}
	}
	return min;
}

void Print(int a[], int n)
{
	cout << "(";
	for (int i = 0; i < n; i++)
	{
		if (i == 2)
		{
			cout << a[i];
			break;
		}
		cout << a[i] << ",";
	}cout <<")" << endl;
}

int main() {
	int S1[] = { -1,0,9 };
	int S2[] = { -25,-10,10,11 };
	int S3[] = { 2,9,17,30,41 };
	int triple[3]; //存放三元组
	int l1 = sizeof(S1) / sizeof(S1[0]);
	int l2 = sizeof(S2) / sizeof(S2[0]);
	int l3 = sizeof(S3) / sizeof(S3[0]);

	cout<<"最短距离为:"<<Getmin(S1, S2, S3,l1,l2,l3,triple)<<endl;

	cout << "三元组为:" << endl;
	Print(triple, 3);

	system("pause");
	return 0;
}

输出结果:√

在这里插入图片描述


😨因此,暴力法在难以剪枝的情况下,效率十分低下 !!!



🎇思路②:找最小

🔱思路分析:

由于 D = ∣ a − b ∣ + ∣ b − c ∣ + ∣ c − a ∣ D=|a-b|+|b-c|+|c-a| D=ab+bc+ca,则有如下结论:

①当 a = b = c a=b=c a=b=c时:距离一定为最小(为0)
②若不满足,则假设 a ≤ b ≤ c a≤b≤c abc,则所求距离如图所示:

在这里插入图片描述

🚀可以看到, L 1 = ∣ b − a ∣ , L 2 = ∣ c − b ∣ , L 3 = ∣ c − a ∣ L1=|b-a|,L2=|c-b|,L3=|c-a| L1=ba,L2=cb,L3=ca D = L 1 + L 2 + L 3 = 2 ∗ L 3 D=L1+L2+L3=2*L3 D=L1+L2+L3=2L3


算法思路:

🔆因此,由D的表达式可知,距离仅取决于最大和最小值,所以问题可以简化为固定c,找到一个a,使 ∣ c − a ∣ |c-a| ca最小

在这里插入图片描述



꧁手算实现꧂:

🚀step:

  1. 由于题目中数组为升序排列,因此,每个数组中最小的元素值即为第一个,则从前往后抽取数组内元素
  2. 对于手算实现,我们其实可以每次固定最小,然后找其他两个集合中比它大又尽可能小的元素,得到当前最小的元素对应的最小距离

我们通过实例来解释:
对于如下升序集合来说:
在这里插入图片描述

我们每一次都找到三个集合中,找到没有被处理过的最小元素,和其他两个集合中比它大的最小元素,得到该最小元素对应的最短距离后,令它所在的数组下标后移一位,继续重复操作,直到存在有数组访问溢出或 a = b = c a=b=c a=b=c


图解分析:

在这里插入图片描述


🎇所以,我们最终会得到前面k个最小元素对应的最短距离

为什么会是它的最短距离?
因为我们固定了最小元素,只需要找到最大的元素,即可确定它的距离,那么,我们尽可能从另外两个集合中抽取尽可能小的两个元素(但要大于所固定的最小元素),这样就使得最大的元素最小,也就得到了最短距离

在这里插入图片描述

🎇最终结束时,集合 S 1 S1 S1中的第三个元素为最小值,已经无法再向右移动,所以结束

因为当有一组超界时,也就是最小值无 a a a法向上增加,那么此时增加 b , c b,c b,c只会使结果更大,将无意义,所以结束



꧁机算实现꧂:

🚀step:

  1. 使用min记录最短距离,初始赋值为极大 ( 0 x 3 f 3 f 3 f ) (0x3f3f3f) 0x3f3f3f
  2. 得到数组 S 1 , S 2 , S 3 S1,S2,S3 S1,S2,S3,令初始指针指向数组的首位置 i = j = k = 0 i=j=k=0 i=j=k=0,当 i < L ( S 1 ) , j < L ( S 2 ) , k < L ( S 3 ) i<L(S1),j<L(S2),k<L(S3) i<L(S1),j<L(S2),k<L(S3)时:

计算距离d,若 d < m i n d<min d<min m i n = d min=d min=d,将当前最小元素对应数组的指针+1,试图1找到更小的距离


💯 时间复杂度:

由于,只有当一个数组全部遍历结束之后才会结束,因此,最坏的情况为,需要遍历数量级为 L m a x = L ( S 1 ) + L ( S 2 ) + L ( S 3 ) L_{max}=L(S1)+L(S2)+L(S3) Lmax=L(S1)+L(S2)+L(S3)的次数,所以时间复杂度为 O ( L m a x ) O(L_{max}) O(Lmax)


🚀功能函数:

①判断最小元素:

//判断此时三元组中的最小值是不是a
bool isMin(int a, int b, int c)
{
	if (a <= b && a <= c)
		return true;
	return false;
}

②得到最短距离:

//功能函数
int Findmin(int a[], int b[], int c[], int l1, int l2, int l3,int *triple)
{
	int i = 0, j = 0, k = 0;
	int min = INF;
	while (i < l1 && j < l2 && k < l3 && min>0)
	{
		int d = abs(a[i] - b[j]) + abs(c[k] - b[j]) + abs(a[i] - c[k]);
		if (d < min)
		{
			min = d;
			triple[0] = a[i]; triple[1] = b[j]; triple[2] = c[k];
		}

		//判断当前作为最小的集合元素
		if (isMin(a[i], b[j], c[k]))
			i++;
		else if (isMin(b[j], a[i], c[k]))
			j++;
		else
			k++;
	}
	return min;
}

注意这里 m i n = = 0 min==0 min==0时也会停下,因为不可能有更小的了


🚀完整代码实现:

#include<iostream>
#include<cmath>
#define INF 0x3f3f3f //定义一个极大值
using namespace std;

//判断此时三元组中的最小值是不是a
bool isMin(int a, int b, int c)
{
	if (a <= b && a <= c)
		return true;
	return false;
}

//功能函数
int Findmin(int a[], int b[], int c[], int l1, int l2, int l3,int *triple)
{
	int i = 0, j = 0, k = 0;
	int min = INF;
	while (i < l1 && j < l2 && k < l3 && min>0)
	{
		int d = abs(a[i] - b[j]) + abs(c[k] - b[j]) + abs(a[i] - c[k]);
		if (d < min)
		{
			min = d;
			triple[0] = a[i]; triple[1] = b[j]; triple[2] = c[k];
		}

		//判断当前作为最小的集合元素
		if (isMin(a[i], b[j], c[k]))
			i++;
		else if (isMin(b[j], a[i], c[k]))
			j++;
		else
			k++;
	}
	return min;
}

//输出
void Print(int a[], int n)
{
	cout << "(";
	for (int i = 0; i < n; i++)
	{
		if (i == 2)
		{
			cout << a[i];
			break;
		}
		cout << a[i] << ",";
	}cout <<")" << endl;
}

int main() {
	int S1[] = { -1,0,9 };
	int S2[] = { -25,-10,10,11 };
	int S3[] = { 2,9,17,30,41 };
	int triple[3];
	int l1 = sizeof(S1) / sizeof(S1[0]);
	int l2 = sizeof(S2) / sizeof(S2[0]);
	int l3 = sizeof(S3) / sizeof(S3[0]);

	cout <<"最短距离为:" << Findmin(S1, S2, S3, l1, l2, l3,triple) << endl;;

	cout << "三元组为:" << endl;
	Print(triple, 3);

	system("pause");
	return 0;
}

输出结果:√

在这里插入图片描述



🎇这是一道关于线性表的操作的真题~🎇

如有错误,欢迎指正~!

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DAY Ⅰ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值