真题题目
定义三元组(a, b, c)(a, b, c均为正数)的距离D = |a-b| + |b-c| + |c-a| 给定3个非空整数集合S1、S2和S3,按升序分别存储在3个数组中。设计一个尽可能高效的算法,计算并输出所有可能的三元组(a, b, c)
(
a
∈
S
1
,
b
∈
S
2
,
c
∈
S
3
)
(a\in S1, b\in S2, c\in S3)
(a∈S1,b∈S2,c∈S3)中的最小距离。
例如S1={-1, 0, 9},S2={-25, -10, 10, 11},S3={2, 9, 17, 30, 41},则最小距离为2,相应的三元组为(9, 10, 9)。要求:
- 给出算法的基本设计思想。
- 根据设计思想,采用C、C++或 Java 语言描述算法,关键之处给出注释。
- 说明你所设计算法的时间复杂度和空间复杂度。
(本文重点关注算法实现思路,不含具体答题表述)
分析实现
首先分析实现本题的中心——三元组的距离D。三元组的距离为三个元素中两两元素的距离之和,即
2
×
(
最大值
−
最小值
)
2\times(最大值-最小值)
2×(最大值−最小值)。
故决定三元组距离大小的是处于边缘的元素,又因为三数组都是升序的,可以通过每次将三个数组中最小的元素“增大一点”(最小元素所在数组对应指针++),并不断记录下扫描过程中的最小距离。这样的策略来得到三元组的最小距离。
具体实现如下:
#include <vector>
#include <cmath>
#define INT_MAX 0x7fffffff
int distance(int a, int b, int c){
return abs(a-b) + abs(b-c) + abs(c-a);
}
int minDistance(vector<int> a, vector<int> b, vector<int> c){
int i=0, j=0, k=0;
int minD = INT_MAX; // 所有三元组的最小距离
int d; // 当前三元组的距离
while(i<a.size() && j<b.size() && k<c.size() && minD>0){
d = distance(a[i], b[j], c[k]); // 计算当前三元组的距离
if(d<minD)
minD = d;
// 三元组中最小的元素向后移动一位
if(a[i]<=b[j] && a[i]<=c[k])
i++;
else if(b[j]<=a[i] && b[j]<=c[k])
j++;
else
k++;
}
return minD;
}
总结
以上就是通过定义三个指针,不断调整最小指针逐步进行扫描,以得出三数组构成三元组最小距离的过程。代码中使用了cmath
头文件中的abs
函数来计算绝对值。
本题的核心在于分析简化三元组的距离公式,根据简化后的公式可以相对顺利地得出优化后的解题方法。
此外(非重点),本篇代码还采用了一个优化的小技巧——尽量不在循环体内定义变量(变量d
特地定义在了循环体之外),因为如果在循环中定义局部变量,每次循环迭代都会进行一次空间的分配和释放,多次重复的操作会使得代码性能有所降低。