《算法导论》拓展之 一维二维最近点对问题

一维点对问题

描述:一维最近点对问题是指在给定的一维点集中找到距离最近的两个点。具体来说,给定一维坐标轴上的 n 个点,要找出其中的两个点,使它们的距离最小。
解决办法:解决这个问题的一种常见方法是使用排序和线性扫描。下面是这种算法的一般步骤:

  1. 排序:将点集按照坐标从小到大进行排序。
  2. 初始化最小距离:设定初始最小距离为正无穷大。
  3. 线性扫描:从左到右遍历排序后的点集。对于每个点,计算它与其右边相邻点的距离,并更新最小距离。
  4. 返回最小距离。
    通过对点集进行排序,可以保证相邻的点在坐标上是接近的,因此只需要线性扫描一次即可找到最小距离的点对。该算法的时间复杂度为 O(n log n),其中 n 是点集中点的数量。
    需要注意的是,在实际实现中,可以使用欧几里得距离公式计算点对之间的距离,并且在处理边界情况时要注意边界的处理和优化,以提高算法的效率。
    总结来说,一维最近点对问题通过排序和线性扫描的方法解决。该算法利用了排序后点在坐标上的接近性,通过线性扫描找到最小距离的点对。
    代码实现:
    下面的这段代码实现了一个找出排序后数组中相邻元素差值最小的算法。其主要思路如下:
  5. 首先定义了一个全局常量 N,用于表示输入数组的最大长度。同时定义了一个数组 A,用于保存输入的数组。
  6. 实现了一个名为 closet_pot 的递归函数,用于在指定区间内查找相邻元素差值的最小值.
    首先,判断区间的大小。如果区间只有一个元素,返回正无穷表示不存在相邻元素。
    • 如果区间只有两个元素,直接返回这两个元素的差值。
    • 否则,将区间分为两个子区间,分别递归调用 closet_pot 函数。
    • 得到左子区间的最小差值 a,右子区间的最小差值 b。
    • 然后取 a 和 b 中的较小值作为当前区间的最小差值 Min。
    • 还需要比较当前区间中相邻两个元素的差值,即 A[mid+1] - A[mid],将其与 Min 比较,更新 Min。
    • 最后,返回 Min 作为当前区间的最小差值。
  7. 在 main 函数中,首先读取输入的数组大小 n。
  8. 然后,通过循环读取 n 个整数,将它们保存到数组 A 中。
  9. 接下来,对数组 A 进行排序,以便找出相邻元素差值的最小值。
  10. 调用 closet_pot 函数,并传入数组的起始下标 0 和结束下标 n-1。
  11. 输出最小差值结果。
  12. 最后,使用 system(“pause”) 命令使程序暂停,防止控制台窗口关闭。
    总结来说,这段代码通过递归的方式,在排序后的数组中查找相邻元素差值的最小值。通过不断地将区间划分为更小的子区间,并比较子区间的最小差值,最终找到整个数组中相邻元素差值的最小值。

#include <iostream>
#include <algorithm>
#include <climits>
#include <vector>
using namespace std;

int closet_pot(vector<int>& A, int p, int q)
{
    if (p == q)
        return INT_MAX;
    if (p == q - 1)
        return A[q] - A[p];

    int mid = (p + q) / 2;
    int a = closet_pot(A, p, mid);
    int b = closet_pot(A, mid + 1, q);

    int Min = min(a, b);
    Min = min(Min, A[mid + 1] - A[mid]);
    return Min;
}

int main()
{
    int n;
    cin >> n;

    vector<int> A(n);
    for (int i = 0; i < n; i++)
        cin >> A[i];

    sort(A.begin(), A.end());

    cout << closet_pot(A, 0, n - 1) << endl;

    return 0;
}

二维点对问题

描述:二维平面上的最近点对问题是指在给定的点集中找到距离最近的两个点对。具体来说,给定平面上的 n 个点,要找出其中的两个点,使它们的距离最小。

解决办法:解决这个问题的一种常见方法是使用分治算法。下面是这种算法的一般步骤:

  1. 排序:按照点的 x 坐标进行排序,从左到右对点集进行排序。
  2. 基本情况处理:如果点集中只有两个或三个点,可以直接计算它们之间的距离,并找到最小距离的点对。
  3. 分割:将点集平均地分成两个子集,左边和右边。取中间点将平面分成两个部分。
  4. 递归求解:对左右两个子集递归地应用最近点对算法,得到左边和右边的最近点对。
  5. 合并:计算左右两个子集的最近点对的距离。然后,从两个子集中选择距离更小的点对作为当前最近点对。
  6. 跨越边界:接下来需要考虑跨越两个子集边界的最近点对。为了找到这些点对,以中间点为界限,向左右两侧延伸一个距离为当前最小距离的区域。
  7. 在跨越边界区域内,使用简单的扫描方法,计算可能的最近点对。由于该区域内的点的数量是有限的,因此复杂度仍然是线性的。
  8. 最后,从左右两个子集的最近点对和跨越边界区域的最近点对中选择距离最小的点对作为最终的最近点对。
    通过分治策略,最近点对问题的时间复杂度可以控制在 O(n log n) 的级别。
    注意:在实际实现中,可以使用欧几里得距离公式计算点对之间的距离,并且在处理边界情况时要注意边界的处理和优化,以提高算法的效率。
    总结:二维平面上的最近点对问题通过将点集进行排序、分割、递归求解、合并和跨越边界的步骤来解决。该算法利用了分治的思想,通过逐步缩小问题规模并处理边界情况,找到平面上距离最近的两个点对。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <limits>

using namespace std;

struct Point {
    double x;
    double y;
};

bool compareX(const Point& p1, const Point& p2) {
    return p1.x < p2.x;
}

bool compareY(const Point& p1, const Point& p2) {
    return p1.y < p2.y;
}

double euclideanDistance(const Point& p1, const Point& p2) {
    double dx = p2.x - p1.x;
    double dy = p2.y - p1.y;
    return sqrt(dx * dx + dy * dy);
}

double bruteForce(const vector<Point>& points, int start, int end) {
    double minDist = numeric_limits<double>::max();
    for (int i = start; i <= end; ++i) {
        for (int j = i + 1; j <= end; ++j) {
            double dist = euclideanDistance(points[i], points[j]);
            minDist = min(minDist, dist);
        }
    }
    return minDist;
}

double closestPairUtil(const vector<Point>& points, int start, int end) {
    if (end - start <= 2) {
        return bruteForce(points, start, end);
    }

    int mid = (start + end) / 2;
    double midX = points[mid].x;

    double dl = closestPairUtil(points, start, mid);
    double dr = closestPairUtil(points, mid + 1, end);
    double d = min(dl, dr);

    vector<Point> strip;
    for (int i = start; i <= end; ++i) {
        if (abs(points[i].x - midX) < d) {
            strip.push_back(points[i]);
        }
    }

    sort(strip.begin(), strip.end(), compareY);

    double stripMin = d;
    int stripSize = strip.size();
    for (int i = 0; i < stripSize; ++i) {
        for (int j = i + 1; j < stripSize && (strip[j].y - strip[i].y) < stripMin; ++j) {
            double dist = euclideanDistance(strip[i], strip[j]);
            stripMin = min(stripMin, dist);
        }
    }

    return min(d, stripMin);
}

double closestPair(const vector<Point>& points) {
    int n = points.size();
    vector<Point> sortedPoints = points;
    sort(sortedPoints.begin(), sortedPoints.end(), compareX);
    return closestPairUtil(sortedPoints, 0, n - 1);
}

int main() {
    vector<Point> points = { {2, 3}, {12, 30}, {40, 50}, {5, 1}, {12, 10}, {3, 4} };
    double minDist = closestPair(points);
    cout << "最近点对的距离: " << minDist << endl;
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 最近问题是指在一维空间上给定n个,找出其中距离最近的两个。为了解决这个问题,可以使用分治算法。 具体步骤如下: 1. 将n个按照x坐标从小到大排序。 2. 将集分成两个部分,分别处理左半部分和右半部分。 3. 对于左半部分和右半部分,分别递归求解最近问题。 4. 计算左半部分和右半部分中距离最近对,取两者中距离最小的对。 5. 如果最近对跨越左右两个部分,则需要考虑跨越两个部分的对。具体方法是,将左右两个部分中的按照y坐标从小到大排序,然后依次计算相邻两个之间的距离,取距离最小的对。 6. 返回距离最近对。 这样,就可以用分治算法求解一维空间上n个最近问题。 ### 回答2: 最近问题,即在一维空间中寻找n个中距离最近的两个,其应用广泛,例如地理位置导航、红外线测距等。分治算法是解决该问题的有效方法之一。 首先,将n个按照位置坐标从小到大排序。接着,将整个数据集分为两个部分,分别从中心把整个数据集分成左右两部分,分别用递归的方法解决左右两部分的最近问题。递归的终止条件是刚好有2个被传入。 用L表示左半部分,R表示右半部分,D表示左右交界处的最短距离。递归求解左半部分得到dL、右半部分得到dR,将二者之间的较小值设为dmin,则暴力求解出左右间隔区域内的最短距离dLR。最后,将dL、dR、dLR中的最小值即为该空间内的最短距离。 理论上,该算法的时间复杂度为O(nlogn)。在实际应用中,对于最近问题分治算法仍然是常用的算法,证明了方法的可行性和可靠性。 ### 回答3: 一维空间上n个最近问题,可以用分治算法来求解。该算法的基本思路是将问题不断划分为小规模的子问题,分别求解并合并得到最终结果。 具体实现步骤如下: 1. 首先将n个按照坐标从小到大排序,得到一个有序集。 2. 将有序集分成左右两个部分,分别处理左右两个部分的最近问题。 3. 对于左右两个部分的最近对,取距离较小的一个作为当前最近对的候选。 4. 接下来考虑横跨两个部分的最近问题。可以将有序集按中心分成两个子集,分别保留左右两个子集内距离中心不超过当前最近对距离的,并按坐标从小到大排序。然后,在两个子集内分别每个向后最多比较6个(可自行修改),找到距离中心小于当前最近对距离的最近对,取距离较小的一个作为当前最近对的候选。 5. 最后,比较候选中的最近对距离和当前最近对距离的大小,取距离较小的一个作为最终结果。 分治算法求解一维空间上n个最近问题,可以在O(nlogn)的时间复杂度内完成。相比于暴力枚举每对的距离O(n^2),使用分治算法的效率大大提高。 需要注意的是,在实际应用中,由于数据随机性等原因,分治算法可能会有误差。可以在程序中设置阈值,当子问题规模小于阈值时,改用暴力方法求解。这样可以兼顾精度和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

KeepCoding♪Toby♪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值