完整教程:2020年_408统考_数据结构41题

题目

定义三元组 (a, b, c)(其中 a, b, c 均为正数)的距离 D = |a - b| + |b - c| + |c - a| 。给定 3 个非空整数集合 S1 、S2 和 S3 ,按升序分别存储在 3 个数组中。请设计一个尽可能高效的算法,计算并输出所有可能的三元组 (a, b, c)(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) 。要求:

  1. 给出算法的基本设计思想。
  2. 根据设计思想, 采用 C 或 C++ 语言描述算法, 关键之处给出注释。
  3. 说明你所设计算法的时间复杂度和空间复杂度。

1.算法基本设计思想

先写出初始数组

在这里插入图片描述
D = |a - b| + |b - c| + |c - a|,这道题的最终结果就是求出 D 的最小值,不妨先画出数轴来帮助我们理解一下这到底是什么意思,这里以S1[2],S2[2],S3[0]为例:

红色代表S1, 绿色代表S2,蓝色代表S3
在这里插入图片描述

在这里插入图片描述
如图:开始推算,|a-b|=1 |b-c|=8 |c-a|=7 相加得出 d = 16,由此得出 D = (最右端 - 最左端 )*2,所以就要求 左端点的值尽可能的大 ,右端点的值尽可能的小(由于数组是递增的,当前选定的右端点一定为最小值),在数轴中小的数在最左边,所需要确认一个a,b,c中的最小值来做左端点,不断的去匹配,例如:
(题目中要求a,b,c大于0,我们示范时忽略这个条件)
在这里插入图片描述
D=(2-(-25))*2 = 54
由于 a.b.c中 b的值最小,所以b做了左端点,继续匹配,b的值是当前的最小值(需要不断偏移左端点,使其变大),存在于S2中,所以更改 b 为 -10
在这里插入图片描述
D=(2-(-10))*2 = 24

继续重复 ,更新b的值为10
在这里插入图片描述
D=(10-(-1))* 2 = 22

此时,左端点变为了 a ,将其更新
在这里插入图片描述
D=(10-0)*2 = 20

继续:
在这里插入图片描述
此时,左端点变为了 c ,更新:

在这里插入图片描述
得出了答案,D=(10-9)*2此时继续更新,同时更新 a,c,发现 a 最高到 9 不能再向后偏移了,也就是说下次如果还是当前左端点最小的话,左端点的值就定死了,只能不断更新右端点(如果按照此例子来解释,就是 当前a已经是 a,b,c中最小的了,由于数组是递增的,那么b和c之后的值一定会更大,不断的更新右端点 )不断向前,最终导致D的值不断增大,继续移动 a 的指针会越界,因此只能移动其他最小值的指针,当至少有一个到达对应数组的末尾时,遍历必须终止:
在这里插入图片描述
D=(17-9)*2 = 16

下来,根据数轴来推断,那么该如何将D变小呢?

推论

当 :大家确定

  • 左端点朝右移,已知其余两个点的值,通过公式D = |a - b| + |b - c| + |c - a|
  • 为什么知道其余两个点的值?
    -当我们不断的移动当前 a (S1 [i])、b (S2 [j])、c (S3 [k]) 中的最小值,若最小值所在数组的指针未越界,则移动该指针,会出现三种情况
    1. 更新的左端点的值还是最小值,其余两个点的值不变,由于数组递增,获取的新左端点一定比之前的左端点值大
    2. 更新的左端点的值在原来右端点和当前左节点(已经替换)之间,当前左节点的值,在未更新前就已经知道,由于更新过后的左端点的值比原来左端点的值大,例如:
    在这里插入图片描述
    更新的左端点的值要在原来右端点和新的左端点(2)之间,所以取更新的左端点为 3
    在这里插入图片描述
    减小的就是重新获取当前左端点,为 2 ,由此可知,获取的当前左端点的值一定是大于上一个左端点,但是右端点还是没有变,所以依旧行确定三个点的位置,通过公式得出D依旧
    3. 更新的左端点的值在原来右端点之后,如图;
    在这里插入图片描述
    按照解释,更新的值应从 -10 更新为大于 5 的值
    - 要是更新的值 等于 17(5+2+10) ,那么D值不变
    - 如果更新的值 小于 17 ,那么D值减小
    - 如果更新的值 大于 17 ,那么D的值增大
    大家每次计算D的之后,都会令其和之前的D比较,发现小于之前的D,更新答案,就避免了此种,还没有遍历完D就增大的情况

算法思路

通过遍历数组,从 0 索引开始,同时获取S1,S2, S3的元素,通过不断的比较,来确定左端点,然后不断的更新最小的数,不断更新左节点,不断的计算D,以获得最终答案

代码实现;

/*************************************************************************
> File Name: solve.cpp
> Author:
> Mail:
> Created Time:
************************************************************************/
/*************************************************************************
* 题目:计算三个有序数组中元素组成的三元组的最小距离
* 算法思路:
* 1. 利用三指针法遍历三个有序数组
* 2. 每次计算当前三个元素的距离 D = |a-b| + |b-c| + |c-a|
* 3. 选择三个元素中最小的那个元素,前进其所在数组的指针
* 4. 重复上述过程直到任一数组遍历完毕
* 5. 在整个过程中记录最小距离
************************************************************************/
#include <iostream>
  #include <cstdlib>
    #include <queue>
      using namespace std;
      int min_num(int a, int b, int c) {
      if (a > b) swap(a, b);
      if (a > c) swap(a, c);
      return a;
      }
      int func(queue<
      int> que1, queue<
      int> que2, queue<
      int> que3) {
      int ans = 0x3f3f3f3f;
      //设置ans
      while (!que1.empty() &&
      !que2.empty() &&
      !que3.empty())
      {
      int a = que1.front(), b = que2.front(), c = que3.front();
      int D = abs(a - b) + abs(b - c) + abs(c - a);
      ans = min(ans, D);
      int new_left = min_num(a, b, c);
      if (a == new_left) que1.pop();
      if (b == new_left) que2.pop();
      if (c == new_left) que3.pop();
      //操作完毕之后已经获得了最新的指针位置
      }
      return ans;
      }
      int main() {
      int m, n, k, x;
      queue<
      int> que1, que2, que3;
      cin >> m >> n >> k;
      for (int i = 0; i < m; i++) {
      cin >> x;
      que1.push(x);
      }
      for (int i = 0; i < n; i++) {
      cin >> x;
      que2.push(x);
      }
      for (int i = 0; i < k; i++) {
      cin >> x;
      que3.push(x);
      }
      cout <<
      func(que1, que2, que3) << endl;
      return 0;
      }

这个算法的时间复杂度为:

时间复杂度:

empty(): O(1) - 队列判断是否为空是常数时间

front(): O(1) - 获取队首元素是常数时间

pop(): O(1) - 弹出队首元素是常数时间

min_num() 中的 swap(): O(1) - 交换两个整数值是常数时间

abs(): O(1) - 绝对值计算是常数时间

min(): O(1) - 比较两个数是常数时间

循环最多执行 (m + n + k) 次

时间复杂度:O(m + n + k)

空间复杂度

空间复杂度:O(m + n + k)

值传递,创建了三个队列的副本就是由于

每个队列分别要求 O(m)、O(n)、O(k) 空间

因此总空间复杂度为 O(m + n + k)

posted @ 2025-09-23 10:33  yxysuanfa  阅读(18)  评论(0)    收藏  举报