【C++】排序中一个岂料之外情理之中的“奇怪”问题

背景

在二维平面上有很多点,目标是给每个点设置一个序号,让人感觉这些序号是从上到下从左到右排列的。
我们用C++实现,假设点的结构如下::

struct Point {
    int x;
    int y;
};

直觉

乍看这个问题好像不难解决,直接给std::sort写一个自定义compare函数,按照点的x、y排序就行了,具体如下:

std::sort(points.begin(), points.end(), [](const Point& a, const Point& b) {
  if (a.y == b.y) { // 垂直方向在同一行,则从左到右排
    return a.x < b.x;
  }
  return a.y < b.y; // 其他情况从上到下排
});

这样也确实解决了问题,能够对点进行从上到下从左到右排序。
在实际应用中有些问题,如下图所示,在视觉上,最左边的两个点的序号应该比较近,也就是下面绿色的排序方案。但按照上述代码排序,会给出上面红色的排序方案。

在这里插入图片描述

调整

那么如何调整,能够实现视觉上的排序呢?自然而然地想到在比较y时增加一定的缓冲空间,具体来说就是两个Point在垂直方向比较接近时( ∣ y 1 − y 2 ∣ < 50 |y_1 - y_2|<50 y1y2<50)时,认为他们处于同一列,按从左到右排列(也就是比较 x x x)。具体实现如下:

// std::vector<Point> points;
std::sort(points.begin(), points.end(), [](const Point& a, const Point& b) {
  if (std::abs(a.y - b.y) < 50) { // 纵向相差50以内,视为同一行 
    return a.x < b.x;
  }
  return a.y < b.y;
});

这种解决方案确实给出了上图绿色区域中的排序,在前期也没有发现有什么问题,但随着点的数量增加,出现了排序结果不一致的问题,也就是对同一个数组多次排序,结果却不相同。

大家觉得可能是什么原因造成的?

这里就要提到std::sortcompare函数的要求:严格弱序 strict weak ordering ,其具体要求有如下3点:

  1. For all a, comp(a, a) == false.
  2. If comp(a, b) == true then comp(b, a) == false.
  3. If comp(a, b) == true and comp(b, c) == true then comp(a, c) == true.

上面给出的compare函数违反了第3个要求,也就是比较的传递性,可能会出现a < bb < ca > c的情况,比如:
a = ( 0 , 51 ) b = ( 1 , 49 ) c = ( 2 , 0 ) a = (0, 51)\\ b = (1, 49)\\ c = (2, 0)\\ a=(0,51)b=(1,49)c=(2,0)

解决

那么想要实现,通过compare函数能够解决吗?单纯实现一个compare函数无法实现我们想要的这种排序,需要组合多个排序来实现

具体来说就是:

  1. 按照的数值对点分组,从上到下每50分为一组,如下图所示
  2. 不同组的点按照从上到下排序
  3. 同一组的点按照从左到右排序
    在这里插入图片描述
    具体实现:
std::vector<Point> sortPoints(std::vector<Point> points, int epsilon) {
    if (points.empty()) return points;

    std::sort(points.begin(), points.end(), [](const Point& a, const Point& b) {
        return a.y < b.y;
    });

    std::vector<std::vector<Point>> groups;
    int start_y = points[0].y;
    std::vector<Point> currentGroup;

    for (const auto& p : points) {
        if (p.y >= (start_y - epsilon)) {
            currentGroup.push_back(p);
        } else {
            groups.push_back(currentGroup);
            currentGroup.clear();
            start_y = p.y;
            currentGroup.push_back(p);
        }
    }
    groups.push_back(currentGroup);

    // 每个组内按x坐标升序排序
    for (auto& group : groups) {
        std::sort(group.begin(), group.end(), [](const Point& a, const Point& b) {
            return a.x < b.x;
        });
    }

    // 合并所有组
    std::vector<Point> result;
    for (const auto& group : groups) {
        result.insert(result.end(), group.begin(), group.end());
    }
    return result;
}

思考

这种方案在根本上解决我们的问题了吗?
答案是否定的,这种按y分组的情况,可能将本来在垂直方向(y)接近的点分在不同组,如下图中的红色点:
在这里插入图片描述

对这个问题,你有更好的解决方案吗?

另外,如果想在比较x时也增加一定的模糊空间,又该如何做呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值