将点作为C++ map容器key值时的踩坑记录

18 篇文章 0 订阅

1.背景

空间点具有X,Y,Z坐标等数据,一些情况下我们需要将点作为map容器的key值,比如识别重复点或处理轮廓等情况。

2.问题

将点作为map的key值,需要自定义比较器或者重载实现点类的小于<操作运算符,判断规则是a < b 和 b < a都为false时,认为a和b相等。

一种比较器或小于运算符重载实现如下,即判断两点在一定误差范围内时,两点相等。

bool PointBorderData::operator< (const PointBorderData& other) const
{
    if (abs(point.X - other.point.X) > 1e-3)
        return point.X < other.point.X;

    if (abs(point.Y - other.point.Y) > 1e-3)
        return point.Y < other.point.Y;

    if (abs(point.Z - other.point.Z) > 1e-3)
        return point.Z < other.point.Z;

    //  相等就 return false
    return false;
}

然后这种实现可能会有问题,有些情况下“相同的点”(在误差范围内)却在map容器中都保留了,经过判断,“认为他们不同”,这是什么原因呢?

聪明的你可能现在也不会想到,直到自己遇到并处理此类问题,或继续看完本文。

3.原因

红框中选中的两个点在误差范围内,应当识别为相同点,在map容器中保留一份,但实际缺各自都被保留了。我们仔细观察其中间的点,嗯,是不是法线问题了?

C++ map容器底层是红黑树,容器元素正向是升序排列,即正序遍历时得到的元素是从最小到最大。

在这里我们姑且称上述3个点为第0、1、2点,第1个点比第0个点小,为什么反而排在第0个点后面?我们对照着前面的代码来分析,第0和1个点x坐标在误差范围内,所以比较y坐标,y值超过了误差范围,且第1点的y值小于第0点,即认为第1点小于第0点,所以它排在了第0点后面。到这里好像也没啥问题,我们继续看~

第3点与第2点比较后,第3点大于第2点,这样好像得到了结论,第0点 < 第1点 < 第2点,得到了3个“不同”的点,那为什么不让第2点和第0点直接比较呢?这样就能识别出它俩相同。

好问题,map底层是红黑树,看下图,

在某些情况下,比如“尝试插入”时的顺序为第1点,第0点,第2点时。在插入第0点时(第1点已插入),其经过一顿比较器执行后需要和第1点比较,因为比较后第0点 < 第1点,所以第0点放在了1点的左分支, 在插入第2点时,(同样经过一顿比较器执行后)到了要和第1点比较的时刻,比较后第1点 < 第2点,第2点放在了第1点的右分支上,整个过程,第2点根本没有机会和第0点比较!所以没有被识别为相同点!

好吧,终于知道问题了。改成下面这样行吗?答案时仍然可能会有问题,具体原因可以自行脑补。

bool PointBorderData::operator< (const PointBorderData& other) const
{
    if (abs(point.X - other.point.X) > 1e-3)
        return point.X < other.point.X;

    if (abs(point.Y - other.point.Y) > 1e-3)
        return point.X == other.point.X ? point.Y < other.point.Y : point.X < other.point.X;

    if (abs(point.Z - other.point.Z) > 1e-3)
        return point.X == other.point.X ? (point.Y == other.point.Y ? point.Z < other.point.Z : point.Y < other.point.Y) : point.X < other.point.X;

    //  相等就 return false
    return false;
}

4.总结

由于点在一定误差内判断为相同,比较好的处理方式是将点坐标保留一定的精度(比如乘1e3,再四舍五入取整)后进行严格数值的大小判断

在有些情况下需要“严格彻底”的识别一定误差内的相同点,比如路径搜寻,就需要有逻辑严格的代码实现,有些情况识别不彻底也不会出错(比如点合并),最多就是多一些“相同”点。最好还是用逻辑准确的代码实现,避免一些超出我们当前认知的“不明确行为”。

欢迎交流:公众号:geometrylib

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值