6.线段求交(学习笔记)

6.1 线段求交

首先对地图叠合问题的最简单形式做一讨论。也就是说,(相互叠合的)两个地图层,分别都是由一组线段表示的某个网络。以图为例,可能有若干小比例图层分别存放道路、铁路和河流信息。

在这里插入图片描述

这些线段将导出的一些子区域,不过在此我们对这些子区域并不感兴趣。后面将会考虑更为复杂的情况——(相互叠合的)地图不是网络,而是平面经过子区域划分之后导出的、具有明确含义的一些子区域。

6.1.1 为解决网络叠合的问题,首先需要用几何的概念来描述这一问题。

就两个网络的叠合而言,对应的几何条件是这样的:给定由线段组成的两个集合,计算出来自于其中一个集合的所有线段,与来自于另一个集合的所有线段之间的交点。

为了做进一步简化,我们还将把分别来自两个集合的线段合到一起,构成一个集合,然后来考虑其中所有线段之间的交点。

进而将问题定义为:给定由平面上n条闭线段构成的一个集合S,报告出S中各线段之间的所有交点。

乍看起来,这个问题并没有什么挑战性——可以依次检查每一对线段,看看它们是否相交;如果的确相交,就将其交点报告出来。显然,这种直截了当式的算法需要O(n2)时间。就某种意义而言,这个结果甚至是最优的:若如图所示,每两条线段都相交,那么无论采用什么算法,至少都需要Ω(n2)时间。

在这里插入图片描述

在实际的环境中,大多数的线段要么根本不与其他线段相交,要么只与少数的线段相交,因此,交点的总数远远达不到平方量级。因此,我们希望得到的算法,其运行时间不仅取决于输入中线段的数目,还取决于(实际的)交点数目。这样的算法,被称为"输出敏感的"算法(output-sensitive-algorithm)。

6.1.2 交点敏感的算法

在找出所有交点的过程中,如何才能避免对所有的线段对进行测试呢?这里必须利用这种情况的几何特性——只有那些相互靠近的线段,才可能会相交;而相距甚远的线段则不可能相交。

设需要对其进行求交的线段构成集合S:={s1,s2,…,sn},首先排除一种简单的情况,如图所示,将一条线段在y轴上的正交投影,定义为它的y区间(y-interval)。

在这里插入图片描述

任何两条线段,只要其y区间没有重叠部分——此时,也可以说,它们在y方向上相距很远——它们就一定不会相交。这样,只需要对那些y区间相互有所重叠(即与同一条垂线相交)的线段对进行测试。

平面扫描算法(plane sweep algorithm)

为找出这些线段对,可以想象着用一条直线l,从一个高于所有线段的位置起,自上而下地扫过整个平面。在这条假象的直线扫过平面的过程中,跟踪记录所有与之相交的线段——以找出所需的所有线段对。

在算法中,使用的直线l被称为扫描线(sweep line)。与当前扫描线相交的所有线段构成的集合,被称为扫描线的状态(status)。

只有在某些特定的位置,才需要对扫描线的状态进行更新。我们称这些位置为平面扫描算法的事件点(event point)。就本算法而言,这里的事件点就是各线段的端点。

事件点

只有在扫描线触及某个事件点的时候,算法才会进行实质的处理——更新扫描线的状态,并进行一些相交测试。具体而言:

  • 上端点

    若事件点为某个线段的上端点,则意味着这条直线段将开始与扫描线相交,因此需要将该线段插入到状态结构(status structure)中。然后,需要将这条线段,和那些与当前扫描线相交的其他线段分别进行测试,确定是否相交。

  • 下端点

    若事件点为某个线段的下断点,则意味着这条线段将不再与扫描线相交,因此需要将该线段从状态结构中删去。

局限性

按照这样的方式,只需要对那些可能与某条水平直线同时相交的线段进行测试。不幸的是,这还不够——因为,在某些(特殊的)情况下,尽管实际的交点数目很少,却依然需要对平方量级的线段进行测试。

一个简单例子就是,所有线段都是垂直的,而且都与x坐标轴相交。因此,目前的这个算法还算不上是输出敏感的。

问题在于,与同一扫描线相交的两条线段,在水平方向上仍然有可能相距很远。

临近性考虑

当考虑到水平方向的临近性后,可以沿着扫描线,将与之相交的所有线段自左向右排序。

这样,只有当其中的某两条线段沿水平方向相邻时,才需要对其进行测试。这就意味着,每引入一条线段,只需要将其与另外的两条线段(具体地讲,就是与新线段上端点左、右紧邻的那两条线段)进行测试。此后,当扫描线向下推进到某个新的位置时,与某条线段紧邻的邻居有可能会发生变化,此时,也需将它与新的邻居进行测试。

在算法的状态结构中,这一新策略应该有所反映——状态结构不仅要记录与当前扫描线相交的所有线段,而且还要对这些线段排序。

在这里插入图片描述

在将这些构思落实为高效的算法之前,需要确定,这种方法正确。验证之前,首先忽略掉一些"棘手"的情况——假设:没有水平线段;任何两条线段最多相交于一点(也就是说,任何两条线段都不会有局部的相互重叠);任何三条线段不会相较于一点。虽然上述假设情况,很容易处理,但是为了更好的论证,目前先置之不理的好。

对于,某线段端点落在另一条线段上的情况,等到扫描线触及相应端点时,这类交点也会被检测。

因此,目前唯一剩下的问题:线段之间的每一交点,是否能被进行检测?

引理

设两条非水平的线段si和sj只相交于其内部的一点p,而且,任何第三条线段都不经过p。则在(扫描线到达)高于p的某个事件点处(时),si和sj必然会彼此紧邻,并因此接受相交测试(于是对应的交点将被发现)。

证明

在这里插入图片描述

如图所示,令l为比p略高的一条水平线。只要l与p相距足够近,则沿着l、si和sj必然是紧邻的(更准确地说,没有任何事件点落在我们所取的l上,而且也没有任何事件点夹在l与通过p的水平线之间)。

总而言之,必然存在某个位置,当扫描线到达这个位置时,si和sj是紧邻的。另一方面,在算法开始的时刻,si和sj并不是紧邻的——此时,扫描线的位置比所有的线段都高,故状态结构还是空的。因此,必然存在某个事件点q,在q的位置,si和sj开始变为紧邻的,从而接受相交测试。

事件点添加新成员

就目前而言,事件点既包括(事先就可以确定的)各线段端点,也包括(在算法运行过程中逐步发现的)交点。

在扫描线移动的过程中,要维护一个有序序列,该序列由所有与当前扫描线相交的线段组成。每遇到一个事件点,都会通过一些动作,对状态结构进行更新,并检测出新的交点——具体的处置方法,取决于事件点类型。

  • 上端点

    事件点为上端点,意味着将有一条新的线段开始与扫描线相交,如下图所示。

    在这里插入图片描述

    新引入的线段,需测试判断它是否与沿扫描线与之紧邻的另外两条线段相交。

    只有位于当前扫描线下方的交点,才需要加以考虑;高于扫描线的交点,必然已经被检测过了。

    例如,线段si和sk原本是紧邻的,而(在某个时刻)第三条线段sj的上端点出现在它们之间,则此时就必须分别将sj与si和sk进行测试。在检测出来的(最多两个)交点中,只要位于当前扫描线的下方,就是一个新的事件点。在处理完该上端点之后,将继续考虑下一个事件点。

  • 交点

    只有位于当前扫描线下方的交点,才是待处理的事件点;位于上方的交点,必然是已经处理完成的事件点。

    在这里插入图片描述

    如图所示,有关的两条相关线段就会交换其(沿扫描线)次序。它们各自可能(最多)有一条新的紧邻线段,因此,必须分别将它们与其各自的新邻居进行测试,以找出可能得交点。

    假设在扫描线触及sk和sl的交点那一时刻,有四条线段sj、sk、sl和sm依次出现在扫描线上。此后,sk和sl将交换次序,于是需要分别对sl和sj、sk和sm进行测试,以找出它们可能位于扫描线下方的交点,并添加为事件点(注意,该事件点可能已经被发现——例如,两条线段在此前的一段时间内曾经是紧邻的,后来变得不再紧邻,最终又再次变成是相互紧邻的)。

  • 下端点

    若事件点为某条线段的下端点,则它此前的(一左一右)两个邻居现在就会变成是相互紧邻的,因此需要对它们进行相交测试。若有交点,且交点位于扫描线的下方,则该交点应添加为事件点(注意,事件点可能已被添加)。

    在这里插入图片描述

    如图,在某一时刻,沿着扫描线,有依次相邻的三条线段sk、sl和sm,扫描线继续前移后遇到了sl的下端点。于是,sk和sm将变成是相互紧邻的。

在平面扫描过程中,如下不变性始终成立:(在任何时刻)处于扫描线上方的所有交点都已经被正确地检测出来。

算法涉及数据结构
  • 平衡二分查找树(balanced binary search tree)

    平衡二分查找树是一种特殊的二叉搜索树,它在插入或删除节点时通过自动调整结构,保持左右子树高度差不超过一定阈值(通常为1)。这种平衡性保证了树的高度始终为O(log n),从而确保查找、插入、删除等操作的时间复杂度稳定在O(log n)。

    常见实现包括:AVL树(由苏联科学家Adelson-Velsky和Landis于1962年发明)、红黑树、Treap等。

    以下是AVL树的一般性实现:

    #include <iostream>
    using namespace std;
    
    class AVLNode{
    public:
        int key;
        AVLNode* left;
        AVLNOde* right;
        int height;
        
        AVLNode(int k):key(k),left(nullptr),right(nullptr),height(1){}
    };
    
    class AVLTree{
    private:
        AVLNode* root;
        
        //获取节点高度
        int getHeight(AVLNode* node){
            return node ? node->height : 0;
        }
        
        //计算平衡因子
        int getBalanceFactor(AVLNode* node){
            return node ? getHeight(node->left) - getHeight(node->right) : 0;
        }
        
        //右旋操作
        /*
            Y(失衡节点)             X(新根节点)
           / \       右旋后         / \
          X   C     ========>     A   Y
         / \                          / \
        A   B                        B   C
        1. 将X的右子树B挂到Y的左子树位置
        2. 将Y挂到X的右子树位置
        */
        AVLNode* rightRotate(AVLNode* y){
            AVLNode* x = y->left;
            AVLNode* T3 = x->right;
            
            x->right = y;
            y->left = T3;
            
            y->height = max(getHeight(y->left),getHeight(y->right)) + 1;
            x->height = max(getHeight(x->left),getHeight(x->right)) + 1;
            
            return x;
        }
        
        //左旋操作
        /* 
              X(失衡节点)               Y(新根节点)
             / \        左旋后          / \
            A   Y      ========>      X   C
               / \                   / \
              B   C                 A   B
          1.将Y的左子树B挂到X的右子树位置
          2.将X挂到Y的左子树位置
        */
        AVLNode* leftRotate(AVLNode* x){
            AVLNode* y = x->right;
            AVLNode* T2 = y->left;
            
            y->left = x;
            x->right = T2;
            
            x->height = max(getHeight(x->left),getHeight(x->right)) + 1;
            y->height = max(getHeight(y->left),getHeight(y->right)) + 1;
            
            return y;
        }
        
        //递归插入节点
        AVLNode* insertHelper(AVLNode* node,int key){
            if(!node) return new AVLNode(key);
            
            if(key < node->key)
                node->left = insertHelper(node->left,key);
            else if(key > node->key)
                node->right = insertHelper(node->right,key);
            else
                return node;	//不允许重复值
            
            //更新高度
            node->height = 1 + max(getHeight(node->left),getHeight(node->right));
            
            //获取平衡因子
            int balance = getBalanceFactor(node);
            
            //平衡调整(4种情况)
            //左左情况:当左子树更高且新节点插入到左子树的左侧
            //右旋
            if(balance > 1 && key < node->left->key)
                return rightRotate(node);
            
            //右右情况:当右子树更高且新节点插入到右子树的右侧
            //左旋
            if(balance < -1 && key > node->right->key)
                return leftRotate(node);
            
            //左右情况:左子树的右子树引发失衡
            //先左后右
            if(balance > 1 && key > node->left->key){
                node->left = leftRotate(node->left);
                return rightRotate(node);
            }
            
            //右左情况:右子树的左子树引发失衡
            //先右后左
            if(balance < -1 && key < node->right->key){
                node->right = rightRotate(node->right);
                return leftRotate(node);
            }
            
            return node;
        }
        
        //中序遍历
        void inorderHelper(AVLNode* node){
            if(!node) return;
            inorderHelper(node->left);
            cout << node->key << " ";
            inorderHelper(node->right);
        }
    public:
        AVLTree() : root(nullptr){}
        void insert(int key){
            root = insertHelper(root,key);
        }
        void printInorder(){
            inorderHelper(root);
            cout << endl;
        }
    }
    
  • 事件队列(event queue)

    来存放(当前已被检测出来,但尚未发生的)事件,使用平衡二分查找树构建。

    • 删除操作

      把即将发生的下一事件从队列中删除掉,并将它返回(给主程序),以便对它进行处理。

      这个事件,是位于扫描线下方、位置最高的那个事件。倘若有两个事件点的y坐标相同,则约定返回x坐标更小的事件点。

      按照这一约定,若是一条水平线段,则应该将其左(右)端点视为上(下)端点。

    • 插入操作

      算法运行的过程中,可能会出现新的事件。请注意,不同事件点的位置可能会重合,位置相同的事件点,当成同一事件点更为合理。

      因此插入操作时,必须检查待插入的事件是否已经出现在队列中。

    在本算法中,事件点作为key值,需要在事件点之间定义一个次序<进行排序。对于任意两个事件点p和q,定义"p<q当且仅当py>qy,或者py = qy且px<qx"。

    对于事件队列中的每一个事件点p,我们同时还应用一个成员变量,记录下起始于p的(也就是以p为其上端点的)那条线段。

    应用平衡二分查找树,做取出下一个事件和插入新的事件的操作,时间复杂度O(logn),n为事件队列中事件点数目。

  • 算法状态

    所谓状态,就是与当前扫描线相交的所有线段构成的有序序列;由于在任一时刻,状态结构中的所有线段之间具有一个定义明确的次序,因此可以使用一棵平衡二分查找树来实现状态结构。

    借助一个状态结构(status structure),可以访问某一给定线段s的(左、右)邻居,插入s之后,可方便的进行相应的相交测试。它是动态的,一旦有某条线段开始(或不再)与扫描线相交,就应将它插入到状态结构中(或从状态结构中删去)。

    在任一时刻,状态结构中的所有线段之间具有一个定义明确的次序,因此可以使用一棵平衡二分查找树来实现状态结构。与当前扫描相交的每条线段,都按照其次序,存放在该平衡二分查找树的某匹叶子处。

    在这里插入图片描述

    如图所示,各线段沿着扫描线自左向右的次序,与状态结构中各叶子自左向右的次序完全一致。在每个内部节点处,要存放其左子树中的最右端叶子。

    假设某个点p正落在扫描线上,我们需要查找紧邻于其左侧的那条线段。在每个内部节点v处,为了判断p是位于线段的左侧还是右侧,只要将p与记录在v处的线段做一次比较。根据比较结果,可以深入到v的左子树或右子树,直到最终到达某匹叶子。

    实践中有另一做法:只将各线段存放在这些内部节点处。这种方法可以节省空间。不过,书籍作者认为,从概念上看,将存放在内部节点处的线段想象成用以引导查找的数值,而不是真正的数据项,将使算法更加易于理解。将线段存放在叶子处,也可以使算法的描述更加简明。

    不过本人水平有限,下面代码示例,用的是线段存放内部节点的做法。

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <limits>
#include <memory>
#include <cassert>
#include <optional>
#include <set>

using namespace std;

#define fTOL 1e-6

// 点结构体
struct Point {
    double x, y;
    
    Point(double x = 0, double y = 0) : x(x), y(y) {}
    
    bool operator==(const Point& other) const {
        return fabs(x - other.x) < fTOL && fabs(y - other.y) < fTOL;
    }
    
    bool operator!=(const Point& other) const {
        return !(*this == other);
    }
};

// 线段结构体
struct Segment {
    Point upper, lower; // 上端点和下端点
    
    Segment(Point p1, Point p2) {
        if (p1.y > p2.y || (p1.y == p2.y && p1.x < p2.x)) {
            upper = p1;
            lower = p2;
        } else {
            upper = p2;
            lower = p1;
        }
    }
    
    // 判断点是否在线段上
    bool contains(const Point& p) const {
        // 检查y坐标范围
        if (p.y < min(lower.y, upper.y) - fTOL || 
            p.y > max(lower.y, upper.y) + fTOL) {
            return false;
        }
        
        // 处理水平线段
        if (fabs(upper.y - lower.y) < fTOL) {
            return p.x >= min(upper.x, lower.x) - fTOL && 
                   p.x <= max(upper.x, lower.x) + fTOL;
        }
        
        // 处理垂直线段
        if (fabs(upper.x - lower.x) < fTOL) {
            return fabs(p.x - upper.x) < fTOL;
        }
        
        // 一般情况:计算参数t并检查x坐标
        double t = (p.y - lower.y) / (upper.y - lower.y);
        if (t < -fTOL || t > 1 + fTOL) return false;
        
        double x = lower.x + t * (upper.x - lower.x);
        return fabs(x - p.x) < fTOL;
    }

    bool operator==(const Segment& other) const {
        return (upper == other.upper && lower == other.lower) ||
               (upper == other.lower && lower == other.upper);
    }

    bool operator<(const Segment& other) const {
        // 比较线段的端点坐标,确保唯一性
        auto comparePoints = [](const Point& a, const Point& b) {
            return (a.x != b.x) ? (a.x < b.x) : (a.y < b.y);
        };

        // 获取当前线段的最小和最大端点
        Point min1 = comparePoints(upper, lower) ? upper : lower;
        Point max1 = comparePoints(upper, lower) ? lower : upper;

        // 获取比较线段的最小和最大端点
        Point min2 = comparePoints(other.upper, other.lower) ? other.upper : other.lower;
        Point max2 = comparePoints(other.upper, other.lower) ? other.lower : other.upper;

        // 先比较最小端点,再比较最大端点
        if (min1 != min2) return comparePoints(min1, min2);
        return comparePoints(max1, max2);
    }

    // 计算两条线段的交点
    static Point* computeIntersection(const Segment& s1, const Segment& s2) {
        // 检查零长度线段
        if (s1.upper == s1.lower || s2.upper == s2.lower) {
            return nullptr;
        }
        
        Point a1 = s1.upper, a2 = s1.lower;
        Point b1 = s2.upper, b2 = s2.lower;
        
        // 特殊处理水平线段
        if (fabs(a1.y - a2.y) < fTOL) { // s1是水平线段
            if (fabs(b1.y - b2.y) < fTOL) { // s2也是水平线段
                // 两条水平线段,检查是否共线且有重叠
                if (fabs(a1.y - b1.y) > fTOL) return nullptr;
                double minX1 = min(a1.x, a2.x);
                double maxX1 = max(a1.x, a2.x);
                double minX2 = min(b1.x, b2.x);
                double maxX2 = max(b1.x, b2.x);
                if (maxX1 < minX2 - fTOL || maxX2 < minX1 - fTOL) return nullptr;
                // 返回任意重叠点
                double x = max(minX1, minX2);
                return new Point(x, a1.y);
            } else {
                // s1水平,s2非水平
                // 检查s2是否与水平线y=a1.y相交
                if ((b1.y - a1.y) * (b2.y - a1.y) > fTOL) return nullptr;
                
                double t = (a1.y - b1.y) / (b2.y - b1.y);
                if (t < -fTOL || t > 1 + fTOL) return nullptr;
                
                double x = b1.x + t * (b2.x - b1.x);
                double minX = min(a1.x, a2.x) - fTOL;
                double maxX = max(a1.x, a2.x) + fTOL;
                if (x >= minX && x <= maxX) {
                    return new Point(x, a1.y);
                }
            }
        } else if (fabs(b1.y - b2.y) < fTOL) { // s2是水平线段
            // s2水平,s1非水平
            if ((a1.y - b1.y) * (a2.y - b1.y) > fTOL) return nullptr;
            
            double t = (b1.y - a1.y) / (a2.y - a1.y);
            if (t < -fTOL || t > 1 + fTOL) return nullptr;
            
            double x = a1.x + t * (a2.x - a1.x);
            double minX = min(b1.x, b2.x) - fTOL;
            double maxX = max(b1.x, b2.x) + fTOL;
            if (x >= minX && x <= maxX) {
                return new Point(x, b1.y);
            }
        } else {
            // 通用情况:两条线段都不是水平的
            double dxa = a2.x - a1.x, dya = a2.y - a1.y;
            double dxb = b2.x - b1.x, dyb = b2.y - b1.y;
            
            double det = dxa * dyb - dya * dxb;
            if (fabs(det) < fTOL) return nullptr; // 平行或共线
            
            double u = ((b1.x - a1.x) * dyb - (b1.y - a1.y) * dxb) / det;
            double v = ((b1.x - a1.x) * dya - (b1.y - a1.y) * dxa) / det;
            
            if (u >= 0 && u <= 1 && v >= 0 && v <= 1) {
                return new Point(a1.x + u * dxa, a1.y + u * dya);
            }
        }
        return nullptr;
    }
};

// 事件点比较函数
struct EventPointCompare {
    bool operator()(const Point& p, const Point& q) const {
        if (p.y != q.y) return p.y > q.y; // y坐标大的优先
        return p.x < q.x; // y相同时,x小的优先
    }
};

// 平衡二叉搜索树节点模板
template <typename T, typename Compare = less<T>>
struct AVLNode {
    T key;
    shared_ptr<AVLNode<T, Compare>> left, right;
    int height;
    shared_ptr<AVLNode<T, Compare>> max_right; // 用于状态结构中存储最右叶子
    
    AVLNode(const T& key) : key(key), left(nullptr), right(nullptr), height(1), max_right(nullptr) {}
};

// 平衡二叉搜索树模板
template <typename T, typename Compare = less<T>>
class AVLTree {
private:
    shared_ptr<AVLNode<T, Compare>> root;
    Compare comp;
    
    // 获取节点高度
    int height(shared_ptr<AVLNode<T, Compare>> node) const {
        return node ? node->height : 0;
    }
    
    // 更新节点高度
    void updateHeight(shared_ptr<AVLNode<T, Compare>> node) {
        if (node) {
            node->height = 1 + max(height(node->left), height(node->right));
        }
    }
    
    // 获取平衡因子
    int balanceFactor(shared_ptr<AVLNode<T, Compare>> node) const {
        return height(node->left) - height(node->right);
    }
    
    // 右旋转
    shared_ptr<AVLNode<T, Compare>> rotateRight(shared_ptr<AVLNode<T, Compare>> y) {
        auto x = y->left;
        auto T2 = x->right;
        
        x->right = y;
        y->left = T2;
        
        updateHeight(y);
        updateHeight(x);
        
        return x;
    }
    
    // 左旋转
    shared_ptr<AVLNode<T, Compare>> rotateLeft(shared_ptr<AVLNode<T, Compare>> x) {
        auto y = x->right;
        auto T2 = y->left;
        
        y->left = x;
        x->right = T2;
        
        updateHeight(x);
        updateHeight(y);
        
        return y;
    }
    
    // 平衡节点
    shared_ptr<AVLNode<T, Compare>> balance(shared_ptr<AVLNode<T, Compare>> node) {
        if (!node) return nullptr;
        
        updateHeight(node);
        
        int bf = balanceFactor(node);
        
        // 左左情况
        if (bf > 1 && balanceFactor(node->left) >= 0) {
            return rotateRight(node);
        }
        
        // 左右情况
        if (bf > 1 && balanceFactor(node->left) < 0) {
            node->left = rotateLeft(node->left);
            return rotateRight(node);
        }
        
        // 右右情况
        if (bf < -1 && balanceFactor(node->right) <= 0) {
            return rotateLeft(node);
        }
        
        // 右左情况
        if (bf < -1 && balanceFactor(node->right) > 0) {
            node->right = rotateRight(node->right);
            return rotateLeft(node);
        }
        
        return node;
    }
    
    // 插入辅助函数
    shared_ptr<AVLNode<T, Compare>> insertHelper(shared_ptr<AVLNode<T, Compare>> node, const T& key) {
        if (!node) return make_shared<AVLNode<T, Compare>>(key);
        
        if (comp(key, node->key)) {
            node->left = insertHelper(node->left, key);
        } else if (comp(node->key, key)) {
            node->right = insertHelper(node->right, key);
        } else {
            return node; // 重复键
        }
        
        return balance(node);
    }
    
    // 查找最小节点
    shared_ptr<AVLNode<T, Compare>> findMin(shared_ptr<AVLNode<T, Compare>> node) const {
        while (node && node->left) {
            node = node->left;
        }
        return node;
    }
    
    // 删除辅助函数
    shared_ptr<AVLNode<T, Compare>> removeHelper(shared_ptr<AVLNode<T, Compare>> node, const T& key) {
        if (!node) return nullptr;
        
        if (comp(key, node->key)) {
            node->left = removeHelper(node->left, key);
        } else if (comp(node->key, key)) {
            node->right = removeHelper(node->right, key);
        } else {
            if (!node->left || !node->right) {
                node = node->left ? node->left : node->right;
            } else {
                auto temp = findMin(node->right);
                node->key = temp->key;
                node->right = removeHelper(node->right, temp->key);
            }
        }
        
        return balance(node);
    }
    
    // 查找辅助函数
    shared_ptr<AVLNode<T, Compare>> findHelper(shared_ptr<AVLNode<T, Compare>> node, const T& key) const {
        if (!node) return nullptr;
        
        if (comp(key, node->key)) {
            return findHelper(node->left, key);
        } else if (comp(node->key, key)) {
            return findHelper(node->right, key);
        } else {
            return node;
        }
    }
    
    // 中序遍历辅助函数
    void inorderHelper(shared_ptr<AVLNode<T, Compare>> node, vector<T>& result) const {
        if (!node) return;
        inorderHelper(node->left, result);
        result.push_back(node->key);
        inorderHelper(node->right, result);
    }

public:
    AVLTree() : root(nullptr), comp() {}
    
    // 插入元素
    void insert(const T& key) {
        root = insertHelper(root, key);
    }
    
    // 删除元素
    void remove(const T& key) {
        root = removeHelper(root, key);
    }
    
    // 查找元素
    bool contains(const T& key) const {
        return findHelper(root, key) != nullptr;
    }
    
    // 获取最小元素
    T findMin() const {
        auto node = findMin(root);
        if (!node) throw runtime_error("Tree is empty");
        return node->key;
    }
    
    // 删除并返回最小元素
    T extractMin() {
        T minVal = findMin();
        remove(minVal);
        return minVal;
    }
    
    // 判断树是否为空
    bool empty() const {
        return !root;
    }
    
    // 中序遍历
    vector<T> inorder() const {
        vector<T> result;
        inorderHelper(root, result);
        return result;
    }
};

// 事件队列
class EventQueue {
private:
    AVLTree<Point, EventPointCompare> tree;
    vector<Segment> segments; // 存储所有线段
    
public:
    // 添加事件点
    void addEvent(const Point& p, const Segment& s) {
        if (!tree.contains(p)) {
            tree.insert(p);
        }
    }
    
    // 获取下一个事件点
    Point nextEvent() {
        if (tree.empty()) {
            throw runtime_error("Event queue is empty");
        }
        return tree.findMin();
    }
    
    // 移除下一个事件点
    Point extractNextEvent() {
        if (tree.empty()) {
            throw runtime_error("Event queue is empty");
        }
        Point p = tree.findMin();
        tree.remove(p);
        return p;
    }
    
    // 判断队列是否为空
    bool empty() const {
        return tree.empty();
    }
    
    // 获取所有事件点
    vector<Point> getAllEvents() const {
        return tree.inorder();
    }
};

// 线段比较函数(用于状态结构)
struct SegmentCompare {
    double scanlineY; // 当前扫描线的y坐标
    
    SegmentCompare() : scanlineY(0) {}
    SegmentCompare(double y) : scanlineY(y) {}
    
    // 比较两条线段在当前扫描线位置的x坐标
    bool operator()(const Segment& s1, const Segment& s2) const {
        double x1 = getXAtScanline(s1);
        double x2 = getXAtScanline(s2);
        return x1 + fTOL < x2;
    }
    
    // 计算线段在扫描线位置的x坐标
    double getXAtScanline(const Segment& s) const {
        if (s.upper.y == s.lower.y) { // 水平线段
            return min(s.upper.x, s.lower.x);
        }
        
        if (fabs(scanlineY - s.upper.y) < fTOL) return s.upper.x;
        if (fabs(scanlineY - s.lower.y) < fTOL) return s.lower.x;
        
        double t = (scanlineY - s.lower.y) / (s.upper.y - s.lower.y);
        return s.lower.x + t * (s.upper.x - s.lower.x);
    }
};

// 算法状态结构
class StatusStructure {
private:
    AVLTree<Segment, SegmentCompare> tree;
    double currentScanlineY;
    
public:
    StatusStructure(double initialY) : currentScanlineY(initialY), tree() {
        tree = AVLTree<Segment, SegmentCompare>();
    }
    
    // 更新扫描线位置
    void updateScanline(double y) {
        currentScanlineY = y;
        // 不再重置整个树,保持现有线段
    }
    
    // 插入线段
    void insert(const Segment& s) {
        tree.insert(s);
    }
    
    // 删除线段
    void remove(const Segment& s) {
        tree.remove(s);
    }
    
    // 查找线段
    bool contains(const Segment& s) const {
        return tree.contains(s);
    }
    
    // 获取线段的前驱
    Segment* predecessor(const Segment& s) const {
        vector<Segment> allSegs = tree.inorder();
        Segment* prev = nullptr;
        SegmentCompare comp(currentScanlineY);
        
        for (auto& seg : allSegs) {
            if (comp(seg, s)) {
                prev = &seg;
            } else {
                break;
            }
        }
        return prev;
    }
    
    // 获取线段的后继
    Segment* successor(const Segment& s) const {
        vector<Segment> allSegs = tree.inorder();
        bool found = false;
         SegmentCompare comp(currentScanlineY);

        for (auto& seg : allSegs) {
            if (found) {
                return &seg;
            }
            if (comp(seg, s)) {
                found = true;
            }
        }
        return nullptr;
    }
    
    // 查找包含点的线段
    Segment* findSegmentContaining(const Point& p) const {
        vector<Segment> allSegs = tree.inorder();
        
        for (auto& seg : allSegs) {
            if (seg.contains(p)) {
                return &seg;
            }
        }
        return nullptr;
    }
    
    // 获取所有线段
    vector<Segment> getAllSegments() const {
        return tree.inorder();
    }
};

// 相交记录结构体
struct IntersectionRecord {
    Segment seg1;
    Segment seg2;
    Point intersection;
    
    IntersectionRecord(const Segment& s1, const Segment& s2, const Point& p)
        : seg1(s1), seg2(s2), intersection(p) {}
        
    bool operator<(const IntersectionRecord& other) const {
        if (fabs(seg1.upper.x - other.seg1.upper.x) >= fTOL) return seg1.upper.x < other.seg1.upper.x;
        if (fabs(seg1.upper.y - other.seg1.upper.y) >= fTOL) return seg1.upper.y < other.seg1.upper.y;
        if (fabs(seg2.upper.x - other.seg2.upper.x) >= fTOL) return seg2.upper.x < other.seg2.upper.x;
        if (fabs(seg2.upper.y - other.seg2.upper.y) >= fTOL) return seg2.upper.y < other.seg2.upper.y;
        if (fabs(intersection.x - other.intersection.x) >= fTOL) return intersection.x < other.intersection.x;
        return intersection.y + fTOL < other.intersection.y;
    }
    
    bool operator==(const IntersectionRecord& other) const {
        return (seg1 == other.seg1 && seg2 == other.seg2 && intersection == other.intersection) ||
               (seg1 == other.seg2 && seg2 == other.seg1 && intersection == other.intersection);
    }
};

// 平面扫描算法
class PlaneSweep
{
private:
    EventQueue eventQueue;
    StatusStructure status;
    vector<Segment> segments;
    set<IntersectionRecord> intersections; // 使用set自动去重

public:
    PlaneSweep(const vector<Segment> &inputSegments) : status(numeric_limits<double>::max())
    {
        segments = inputSegments;

        // 初始化事件队列
        for (const auto &seg : segments)
        {
            eventQueue.addEvent(seg.upper, seg); // 上端点
            eventQueue.addEvent(seg.lower, seg); // 下端点
        }
    }

    // 运行平面扫描算法
    void run()
    {
        while (!eventQueue.empty())
        {
            Point p = eventQueue.extractNextEvent();
            cout << "\nProcessing event at (" << p.x << ", " << p.y << ")" << endl;

            // 更新扫描线位置
            status.updateScanline(p.y);

            // 处理事件点
            handleEventPoint(p);

            // 打印当前状态
            cout << "Current segments in status:" << endl;
            vector<Segment> currentSegs = status.getAllSegments();
            for (const auto& seg : currentSegs) {
                cout << "(" << seg.upper.x << "," << seg.upper.y << ")-(" 
                     << seg.lower.x << "," << seg.lower.y << ")" << endl;
            }
        }
    }

    // 获取所有相交记录
    set<IntersectionRecord> getIntersections() const {
        return set<IntersectionRecord>(intersections.begin(), intersections.end());
    }

private:
    // 处理事件点
    void handleEventPoint(const Point &p)
    {
        vector<Segment> U, L, C;

        // 分类线段
        for (const auto &seg : segments)
        {
            if (seg.upper == p)
            {
                U.push_back(seg); // 以p为上端点的线段
            }
            else if (seg.lower == p)
            {
                L.push_back(seg); // 以p为下端点的线段
            }
            else if (seg.contains(p))
            {
                // 对于水平线段,如果p在端点,已经包含在U/L中
                if (!(fabs(seg.upper.y - seg.lower.y) < fTOL && 
                     (p == seg.upper || p == seg.lower))) {
                    C.push_back(seg); // 包含p的线段
                }
            }
        }
        
        // 特殊处理水平线段
        for (const auto &seg : segments) {
            if (fabs(seg.upper.y - seg.lower.y) < fTOL && 
                seg.contains(p)) {
                // 水平线段且包含当前事件点
                if (!(seg.upper == p || seg.lower == p)) {
                    C.push_back(seg);
                }
                // 检查与所有当前活动线段的交点
                for (const auto& activeSeg : status.getAllSegments()) {
                    if (fabs(activeSeg.upper.y - activeSeg.lower.y) > fTOL) { // 非水平线段
                        Point* intersection = Segment::computeIntersection(seg, activeSeg);
                        if (intersection && *intersection != p) {
                            bool exists = false;
                            for (const auto& record : intersections) {
                                if ((record.seg1 == seg && record.seg2 == activeSeg) ||
                                    (record.seg1 == activeSeg && record.seg2 == seg)) {
                                    exists = true;
                                    break;
                                }
                            }
                            if (!exists) {
                                intersections.insert(IntersectionRecord(seg, activeSeg, *intersection));
                                eventQueue.addEvent(*intersection, seg);
                            }
                            delete intersection;
                        }
                    }
                }
            }
        }

        // 处理线段交点
        if (U.size() + L.size() + C.size() > 1) {
            // 检查所有可能的线段对组合
            vector<Segment> allSegs;
            allSegs.insert(allSegs.end(), U.begin(), U.end());
            allSegs.insert(allSegs.end(), L.begin(), L.end());
            allSegs.insert(allSegs.end(), C.begin(), C.end());
            
            // 使用set去重线段并遍历所有唯一组合
            set<Segment> uniqueSegs(allSegs.begin(), allSegs.end());
            vector<Segment> uniqueVec(uniqueSegs.begin(), uniqueSegs.end());
            
            for (size_t i = 0; i < uniqueVec.size(); ++i) {
                for (size_t j = i + 1; j < uniqueVec.size(); ++j) {
                    // 检查是否已经记录过这个交点
                    bool exists = false;
                    for (const auto& record : intersections) {
                        if ((record.seg1 == uniqueVec[i] && record.seg2 == uniqueVec[j]) ||
                            (record.seg1 == uniqueVec[j] && record.seg2 == uniqueVec[i])) {
                            exists = true;
                            break;
                        }
                    }
                    if (!exists) {
                        intersections.insert(IntersectionRecord(uniqueVec[i], uniqueVec[j], p));
                    }
                }
            }
        }

        // 从状态结构中移除L和C线段
        for (const auto &seg : L)
        {
            if (status.contains(seg))
            {
                status.remove(seg);
            }
        }
        for (const auto &seg : C)
        {
            if (status.contains(seg))
            {
                status.remove(seg);
            }
        }

        // 将U和C线段插入状态结构
        for (const auto &seg : U)
        {
            status.insert(seg);
        }
        for (const auto &seg : C)
        {
            status.insert(seg);
        }

        // 检查所有相邻线段的交点
        vector<Segment> currentSegments = status.getAllSegments();
        // cout << "===================" << endl;
        // cout << "EventPt: (" << p.x << "," << p.y << ")" << endl;
        // cout << "currentSegments Num:" << currentSegments.size() << endl;
        for (size_t i = 1; i < currentSegments.size(); i++)
        {
            Segment *left = &currentSegments[i - 1];
            Segment *right = &currentSegments[i];

            // cout << "intersection left seg: (" << left->upper.x << "," << left->upper.y << ")-("
            //      << left->lower.x << "," << left->lower.y << ")" << endl;
            // cout << "intersection right seg: (" << right->upper.x << "," << right->upper.y << ")-("
            //      << right->lower.x << "," << right->lower.y << ")" << endl;

            Point *intersection = Segment::computeIntersection(*left, *right);
            if (intersection && *intersection != p)
            { // 避免重复处理当前事件点
                //  cout << "Found internal intersection at (" << intersection->x
                //       << ", " << intersection->y << ")" << endl;
                intersections.insert(IntersectionRecord(*left, *right, *intersection));
                // 只添加一次交点事件
                eventQueue.addEvent(*intersection, *left);
                delete intersection;
            }
        }
        // 获取新插入线段的最左和最右
        Segment *leftMost = nullptr;
        Segment *rightMost = nullptr;

        if (!U.empty())
        {
            leftMost = &U.front();
            rightMost = &U.back();
        }
        if (!C.empty())
        {
            if (!leftMost || SegmentCompare(p.y)(C.front(), *leftMost))
            {
                leftMost = &C.front();
            }
            if (!rightMost || SegmentCompare(p.y)(*rightMost, C.back()))
            {
                rightMost = &C.back();
            }
        }

        // 检查与新邻居的交点
        if (leftMost)
        {
            Segment *leftNeighbor = status.predecessor(*leftMost);
            if (leftNeighbor)
            {
                Point *intersection = Segment::computeIntersection(*leftNeighbor, *leftMost);
                if (intersection && *intersection != p)
                {
                    //  cout << "Found intersection at (" << intersection->x
                    //       << ", " << intersection->y << ")" << endl;
                    // // 记录相交信息
                    //  cout << "leftNeighbor Adding to intersection: (" << leftNeighbor->upper.x << "," << leftNeighbor->upper.y << ")-("
                    //  << leftNeighbor->lower.x << "," << leftNeighbor->lower.y << ")" << endl;
                    //  cout << "leftMost Adding to intersection: (" << leftMost->upper.x << "," << leftMost->upper.y << ")-("
                    //  << leftMost->lower.x << "," << leftMost->lower.y << ")" << endl;
                    intersections.insert(IntersectionRecord(*leftNeighbor, *leftMost, *intersection));
                    eventQueue.addEvent(*intersection, *leftNeighbor);
                    delete intersection;
                }
            }
        }

        if (rightMost)
        {
            Segment *rightNeighbor = status.successor(*rightMost);
            if (rightNeighbor)
            {
                Point *intersection = Segment::computeIntersection(*rightMost, *rightNeighbor);
                if (intersection && *intersection != p)
                {
                    //  cout << "Found intersection at (" << intersection->x
                    //       << ", " << intersection->y << ")" << endl;
                    // // 记录相交信息
                    // cout << "rightMost Adding to intersection: (" << rightMost->upper.x << "," << rightMost->upper.y << ")-("
                    //  << rightMost->lower.x << "," << rightMost->lower.y << ")" << endl;
                    //  cout << "rightNeighbor Adding to intersection: (" << rightNeighbor->upper.x << "," << rightNeighbor->upper.y << ")-("
                    //  << rightNeighbor->lower.x << "," << rightNeighbor->lower.y << ")" << endl;
                    intersections.insert(IntersectionRecord(*rightMost, *rightNeighbor, *intersection));
                    eventQueue.addEvent(*intersection, *rightMost);
                    delete intersection;
                }
            }
        }
    }
};

int main() {
    // 创建测试线段
    vector<Segment> segments = {
        Segment(Point(1, 5), Point(4, 2)), // 线段1
        Segment(Point(3, 6), Point(6, 2)), // 线段2
        Segment(Point(2, 4), Point(5, 6)), // 线段3
        Segment(Point(0, 3), Point(7, 3))  // 水平线段
    };
    
    // 运行平面扫描算法
    PlaneSweep ps(segments);
    ps.run();
    
    // 打印所有相交记录
    cout << "\nIntersection Report:" << endl;
    cout << "===================" << endl;
    cout << "Total intersections found: " << ps.getIntersections().size() << endl;
    cout << "===================" << endl;
    for (const auto& record : ps.getIntersections()) {
        cout << "\nSegment 1: (" << record.seg1.upper.x << ", " << record.seg1.upper.y << ")-("
             << record.seg1.lower.x << ", " << record.seg1.lower.y << ")" << endl;
        cout << "Segment 2: (" << record.seg2.upper.x << ", " << record.seg2.upper.y << ")-("
             << record.seg2.lower.x << ", " << record.seg2.lower.y << ")" << endl;
        cout << "Intersection Point: (" << record.intersection.x << ", " << record.intersection.y << ")" << endl;
        cout << "-------------------" << endl;
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值