• 最大重叠点


    题目:

    分析与思考:

                这个问题事实上不用用b部分提示的那么做,感觉比較复杂。我感觉这样的方法不错:首先将n个区间的2n个端点从小到大排序;用一个集合S(顺序统计树。集合中的元素以区间左端点的大小为序)来记录当前发生重叠的区间,初始化S为空。
    遍历2n个端点
    {
        假设该点为某区间左端点
        {
            将相应区间增加S中;
            该区间在S中的(顺序统计)位置。就是该(左端)点相应的重叠个数,如有必要则更新最大重叠个数。
        }
        假设该点为某区间右端点
        {
            将相应区间从S中删除。
        }

    }
     这种方法就是求矩形重叠。仅仅只是从推断矩形重叠是否存在后就返回,变为每次插入结点后,就推断结点在区间树的位置,这个位置就是重叠数。然后不断更新最大重叠结点,最后等着2n个端点都遍历完了,就知道当中的最大重叠端点。

    程序代码:

    归并排序头文件:

     struct Array
    {
    	int key;
    	int index;
    };
    void MERGE(struct Array B[],int p,int q,int r)
    {
        int n1=q-p+1,n2=r-q,flag=-1,i,j;//不能为数组A里面的数。
        struct Array *L=new struct Array[n1];
        struct Array *R=new struct Array[n2];
        for (i=1;i<=n1;i++)
        {
            L[i-1].key=B[p+i-1].key;
    		L[i-1].index=B[p+i-1].index;
        }
        for (j=1;j<=n2;j++)
        {
            R[j-1].key=B[q+j].key;
    		R[j-1].index=B[q+j].index;
        }
        L[n1].key=flag;
        R[n2].key=flag;
        i=0;j=0;
        for (int k=p;k<=r;k++)
        {
            if (L[i].key==flag)
            {
                B[k].key=R[j].key;
    			B[k].index=R[j].index;
                j++;
            }
            else if (R[j].key==flag)
            {
                B[k].key=L[i].key;
                B[k].index=L[i].index;
                i++;
            }
            else if (L[i].key<=R[j].key)
            {
                B[k].key=L[i].key;
    			B[k].index=L[i].index;
                i++;
            }
            else 
            {
                B[k].key=R[j].key;
    			B[k].index=R[j].index;
                j++;
            }
        }
    }
    void MERGE_SORT(struct Array B[],int p,int r)
    {
        if (p<r)
        {
            int q=(p+r)/2;
            MERGE_SORT(B,p,q);
            MERGE_SORT(B,q+1,r);
            MERGE(B,p,q,r);
        }
    }

    主函数+区间树:

    #include <iostream>
    #include <conio.h>
    #include "MERGE_SORT.h"
    using namespace std;
    #define BLACK 0
    #define RED 1
    #define Nil -1
    #define LEN sizeof(struct Tree)
    #define n 12//区间个数
    struct Tree*root=NULL;
    struct Tree*nil=NULL;
    struct interval
    {
    	int low,high;
    };
    struct Tree
    {
    	struct Tree*right,*left;
    	struct Tree*parent;
    	struct interval Int;
    	int flag;//1表示已经走过了该区间的左端点。0表示还未到左端点
    	int Max;
    	int key;
    	int color;
    	int size;
    };
    int MAX(int a,int b,int c)
    {
    	int temp=a>b?a:b;
    	return temp>c?temp:c;
    }
    void LEFT_ROTATE(struct Tree*x)
    {//左旋转:分三个步骤①②③来叙述旋转代码的。
    	struct Tree*y=x->right;//设置y结点。

    x->right=y->left;//本行代码以及以下的if结构表达的是“y的左孩子成为x的右孩子”。① if(y->left!=nil) { y->left->parent=x; } y->parent=x->parent;//本行代码以及以下的if-else结构表达的过程是“y成为该子树新的根”。

    ② if(x->parent==nil) { root=y; } else if(x==x->parent->left) { x->parent->left=y; } else x->parent->right=y; y->left=x;//本行代码以及以下一行都表达了“x成为y的左孩子”。③ x->parent=y; y->Max=x->Max; x->Max=MAX(x->Int.high,x->left->Max,x->right->Max); y->size = x->size; //对附加信息的维护 x->size = x->left->size + x->right->size +1; } void RIGHT_ROTATE(struct Tree*x) {//右旋转:分三个步骤①②③来叙述旋转代码的。 struct Tree*y=x->left;//设置y结点。 x->left=y->right;//本行代码以及以下的if结构表达的是“y的左孩子成为x的右孩子”。

    ① if(y->right!=nil) { y->right->parent=x; } y->parent=x->parent;//本行代码以及以下的if-else结构表达的过程是“y成为该子树新的根”。② if(x->parent==nil) { root=y; } else if(x==x->parent->right) { x->parent->right=y; } else x->parent->left=y; y->right=x;//本行代码以及以下一行都表达了“x成为y的左孩子”。

    ③ x->parent=y; y->Max=x->Max; x->Max=MAX(x->Int.high,x->left->Max,x->right->Max); y->size = x->size; //对附加信息的维护 x->size = x->left->size + x->right->size +1; } void RB_INSERT_FIXUP(struct Tree*z) { while (z->parent->color==RED) { if (z->parent==z->parent->parent->left) { struct Tree*y=z->parent->parent->right;//叔结点 if (y->color==RED)//情况一:叔结点为红色 {//给p1,y,p2着色以保持性质5。

    而且攻克了z的父结点和z都是红色结点问题 z->parent->color=BLACK; y->color=BLACK; z->parent->parent->color=RED; z=z->parent->parent;//把z的祖父结点当成新结点z进入下一次循环 } else { if (z==z->parent->right)//情况二:检查z是否是一个右孩子且叔结点为黑色,前提是p1结点不是叶子结点 {//使用一个左旋让情况2转变为情况3 z=z->parent; LEFT_ROTATE(z);//因为进入if语句后可知旋转结点不可能是叶子结点,这样就不用推断z是否是叶子结点了。 } z->parent->color=BLACK;//情况三:是z是一个左孩子且叔结点为黑色,改变z的父和祖父结点颜色并做一次右旋,以保持性质5 z->parent->parent->color=RED; RIGHT_ROTATE(z->parent->parent);//因为p2可能是叶子结点,所以不妨用一个if推断 } } else//以下else分支相似于上面,注意到else分支的情况2和情况3所做旋转正好是if分支情况的逆。 { struct Tree*y=z->parent->parent->left; if (y->color==RED) { z->parent->color=BLACK; y->color=BLACK; z->parent->parent->color=RED; z=z->parent->parent; } else { if (z==z->parent->left) { z=z->parent; RIGHT_ROTATE(z); } z->parent->color=BLACK; z->parent->parent->color=RED; LEFT_ROTATE(z->parent->parent); } } } root->color=BLACK;//最后给根结点着为黑色。

    } void RB_INSERT(struct Tree* z) { z->key=z->Int.low; struct Tree*y=nil; struct Tree*x=root; while (x!=nil) { y=x; x->size++; x->Max=MAX(x->Int.high,x->Max,z->Int.high); if (z->key<x->key) { x=x->left; } else x=x->right; } z->parent=y; if (y==nil) { root=z; } else if(z->key<y->key) { y->left=z; } else y->right=z; z->left=nil;//给插入结点左右孩子赋值为空。 z->right=nil; z->color=RED;//给插入结点着为红色。 z->Max=z->Int.high;//+ z->size=1; z->left->size=0; z->right->size=0; RB_INSERT_FIXUP(z); } void RB_TRANSPLANT(struct Tree*u,struct Tree*v) { if (u->parent==nil) root=v; else if(u==u->parent->left) u->parent->left=v; else u->parent->right=v; v->parent=u->parent; } //非递归版本号的查找二叉查找树的最小值 struct Tree*ITERATIVE_TREE_MINIMUM(struct Tree*x) { while (x->left!=nil) { x=x->left; } return x; } //非递归版本号的二叉查找树查找函数 struct Tree*ITERATIVE_TREE_SEARCH(struct Tree*x,int k) { while (x!=nil&&k!=x->key) { if (k<x->key) { x=x->left; } else x=x->right; } return x; } void RB_DELETE_FIXUP(struct Tree*x) { struct Tree*w=NULL;//w是x的兄弟结点 while (x!=root&&x->color==BLACK)//假设x是黑色而且不是根结点,才进行循环。 {//x是一个具有双重颜色的结点,调整的目的是把x的黑色属性向上移动。 if (x==x->parent->left) { w=x->parent->right; if (w->color==RED)//情况一:x的兄弟结点w是红色的。

    {//改变w和x.p的颜色+一次旋转使其变为情况二。三,四。 w->color=BLACK; x->parent->color=RED; LEFT_ROTATE(x->parent); w=x->parent->right; } if (w->left->color==BLACK&&w->right->color==BLACK)//情况二:x的兄弟结点w是黑色的,而且w的两个子节点都是黑色。 { w->color=RED;//从x和w上去掉一重黑色。x还是黑色,而w变为红色。 x=x->parent;//x结点向上移动成为新的待调整结点。 } else { if (w->right->color==BLACK)//情况三:x的兄弟结点w是黑色的。w的左孩子是红色的。w的右孩子是黑色的。 {//交换w和w.left的颜色+旋转使其转变为情况四。

    w->left->color=BLACK; w->color=RED; RIGHT_ROTATE(w); w=x->parent->right; } w->color=x->parent->color;//以下是情况四:x的兄弟结点w是黑色的。且w的右孩子是红色的。 x->parent->color=BLACK;//置x.p和w.right为黑色+旋转使其去掉x的额外黑色。 w->right->color=BLACK; LEFT_ROTATE(x->parent); x=root;//x成为根结点,结束循环。

    } } else//以下和上面的if分支相似,不做累述。

    { w=x->parent->left; if (w->color==RED) { w->color=BLACK; x->parent->color=RED; RIGHT_ROTATE(x->parent); w=x->parent->left; } if (w->left->color==BLACK&&w->right->color==BLACK) { w->color=RED; x=x->parent; } else { if (w->left->color==BLACK) { w->right->color=BLACK; w->color=RED; LEFT_ROTATE(w); w=x->parent->left; } w->color=x->parent->color; x->parent->color=BLACK; w->left->color=BLACK; RIGHT_ROTATE(x->parent); x=root; } } } x->color=BLACK; } void RB_DELETE(struct Tree*z) { struct Tree*y=z,*x;//y为待删除或待移动结点 int y_original_color=y->color;//保存y的原始颜色,为做最后的调整做准备。 struct Tree*t=z->parent; if (z->left==nil) { while (t!=nil) { t->size--; t=t->parent; } x=z->right;//x指向y的唯一子结点或者是叶子结点。保存x的踪迹使其移动到y的原始位置上 RB_TRANSPLANT(z,z->right);//把以z.right为根的子树替换以z为根的子树。 } else if (z->right==nil) { while (t!=nil) { t->size--; t=t->parent; } x=z->left;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上 RB_TRANSPLANT(z,z->left);//把以z.left为根的子树替换以z为根的子树。 } else { y=ITERATIVE_TREE_MINIMUM(z->right);//找到z.right的后继。

    struct Tree*t=y->parent; y->size=z->size-1;//y替换z原来的位置。所以size属性在待删除结点z基础上-1 while (t!=nil) { t->size--; t=t->parent; } y_original_color=y->color;//y的新的原始结点被重置。

    x=y->right;//x指向y的唯一子结点或者是叶子结点。保存x的踪迹使其移动到y的原始位置上 if (y->parent==z) { x->parent=y;//因为z的父结点是要删除的结点。所以不能指向它,于是指向y } else { RB_TRANSPLANT(y,y->right);//把以y.right为根的子树替换以y为根的子树。

    y->right=z->right; y->right->parent=y; } RB_TRANSPLANT(z,y);//把以y为根的子树替换以z为根的子树。 y->left=z->left; y->left->parent=y; y->color=z->color;//把已经删除的结点颜色赋值给y,保证了y以上的树结构红黑性质不变。 } struct Tree*k=x->parent; while (k!=nil) { k->Max=MAX(k->left->Max,k->right->Max,k->Int.high); k=k->parent; } if(y_original_color==BLACK) //y的原始颜色为黑色,说明须要调整红黑颜色。

    RB_DELETE_FIXUP(x); } bool overlap(struct interval x,struct interval i) { if (x.high<i.low||i.high<x.low) { return true;//没有重叠 } else { return false; } } struct Tree *INTERVAL_SEARCH(struct Tree *T,struct interval i) { struct Tree *x=T; while (x!=nil&&overlap(x->Int,i)) { if (x->left!=nil&&x->left->Max>=i.low) { x=x->left; } else x=x->right; } return x; } struct Tree*OS_SELECT(struct Tree*x,int i)//查找顺序统计树给定秩的元素 { int r=x->left->size+1; if (i==r) { return x; } else if (i<r) { return OS_SELECT(x->left,i); } else return OS_SELECT(x->right,i-r); } int ITERATIVE_OS_RANK(struct Tree*T,struct Tree*x)//确定顺序统计树的秩 { int r=x->left->size+1; struct Tree*y=x; while (y!=root) { if (y==y->parent->right) { r=r+y->parent->left->size+1; } y=y->parent; } return r; } struct Tree* FIND_POM(struct Tree A[],struct Array B[]) {//推断n个矩阵是否重叠,执行时间为O(nlgn) int i=1,t=0,M=0; struct Tree*MAX=NULL; while (i!=2*n) { if (A[B[i].index].flag==0)//0代表矩形Ri的纵坐标的还未进入扫描线。

    { struct Tree*z=new struct Tree[LEN]; z->key=A[B[i].index].Int.low; z->Int.low=A[B[i].index].Int.low; z->Int.high=A[B[i].index].Int.high; A[B[i].index].flag=1; RB_INSERT(z);//将这个矩形插入进区间树 t=ITERATIVE_OS_RANK(root,z); if (t>M) { M=t; MAX=z; } } else//否则,矩形Ri的纵坐标进入过扫描线了,那么遇到的横坐标(B[i].key代表横坐标)必定是Ri的高端点。 { if (i==2*n-1||A[B[i+1].index].Int.low!=A[B[i].index].Int.high) { struct Tree*z=ITERATIVE_TREE_SEARCH(root,A[B[i].index].Int.low);//先找到区间树中的这个结点。 RB_DELETE(z);//从区间树中删除这个矩形。 } } i++; } return MAX; } void init(struct Tree A[],struct Array B[]) {//区间树初始化。

    nil=new struct Tree[LEN];//设置叶子结点 nil->key=Nil;nil->color=BLACK; root=nil; int i=0; struct Tree*z=new struct Tree[LEN];//设置根结点。 z->key=A[B[i].index].Int.low; z->Int.low=A[B[i].index].Int.low; z->Int.high=A[B[i].index].Int.high; RB_INSERT(z); root=z; A[B[i].index].flag=1; } void main() { struct Tree A[n]={0}; struct Array B[2*n]={0}; for (int i=0,j=0;i<n,j<2*n;i++,j+=2) { cout<<"请输入第"<<i<<"个矩阵的数据:"<<endl; cout<<" y的低端点=";cin>>A[i].Int.low; cout<<" y的高端点=";cin>>A[i].Int.high; B[j].key=A[i].Int.low; B[j+1].key=A[i].Int.high; B[j].index=i; B[j+1].index=i; } MERGE_SORT(B,0,2*n-1);//归并排序。时间为O(nlgn) init(A,B); cout<<FIND_POM(A,B)->key<<endl; }

    总结:

             和14.3-7求重叠矩形类似,换汤不换药。由于要循环遍历这2n个端点,而且每次遍历时要进行插入删除操作顺便记录最大值,所以执行时间是O(nlgn),而《教师手冊》说FIND_POM函数仅仅须要O(1)时间。这是由于没有算上插入的时间。假设要查找这n个区间的最大重叠点肯定要进行插入组成一棵树。总的时间不会少于O(nlgn)的,而本文的FIND_POM操作把组建这棵树的时间也算在内了。

    总得来说,两种方法几乎相同,仅仅是本文的方法比較简单易懂罢了。

    參考资料:这里有相关问题的讨论

    
  • 相关阅读:
    Funny Car Racing
    [LDUoj 倍增] 题解
    [HDU7073] Integers Have Friends 2.0 -随机大法好
    【spring】全局异常 globalexception 处理
    【maven】测试
    【spring】spring aop
    jvm常用排错命令
    idea tools
    idea插件
    【maven】搭建maven私服--基于CentOS7.6+docker
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/7211254.html
Copyright © 2020-2023  润新知