一、题目
二、思考
采用红黑树作为基础数据结构,扩展为14.3中的区间树。
第一步:
对每个矩形的左边x和右边x排序,排序结果放在一个序列中。并记录每个x值属于哪个矩形。
如三个矩形的x区间(左边x,右边x)分别是(1,3),(2,4),(3,5),排序结果(x值,属于哪个矩形)是(1,1),(2,2),(3,1),(3,3),(4,2),(5,3)。
第二步:
依次处理排序结果(x,i)。
(1)如果x是矩形i的左边x,则求出区间树中与矩形i的y区间相交的区间个数,再把矩形i的y区间插入到区间树中。
(2)如果x是矩形i的右边x,则将矩形i的y区间从区间树中删除
三、代码
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define BLACK 0
- #define RED 1
- #define N 10//矩形的个数
- //区间结构
- struct interval
- {
- int low;
- int high;
- interval(){};
- interval(int l, int h):low(l), high(h){}
- bool operator==(interval &b)
- {
- if(low == b.low && high == b.high)
- return 1;
- return 0;
- }
- };
- //矩形结构
- struct Rectangle
- {
- interval x;
- interval y;
- };
- //用于排序
- struct Sort_Node
- {
- int x;
- int id;//x所属的矩形的编号
- Sort_Node(){}
- Sort_Node(int a, int b):x(a),id(b){}
- };
- //用于排序
- bool cmp(Sort_Node a, Sort_Node b)
- {
- return a.x < b.x;
- }
- //区间树结点结构
- struct node
- {
- node *left;
- node *right;
- node *p;
- bool color;
- interval inte;//仅存储矩形的y区间
- int max;
- node(node *init, interval i):left(init),right(init),p(init),inte(i),max(i.high),color(BLACK){}
- };
- //区间树结构
- struct Interval_Tree
- {
- node *root;//根结点
- node *nil;//哨兵
- Interval_Tree(){nil = new node(NULL, interval(0,-0x7fffffff));root = nil;};
- };
- //a和b是否重叠,若重叠,返回1
- bool Overlap(interval a, interval b)
- {
- //a在b的左边
- if(a.high < b.low)
- return 0;
- //a在b的右边
- if(a.low > b.high)
- return 0;
- return 1;
- }
- //用于维护信息
- int max(int a,int b,int c)
- {
- if(a > b)
- return a > c ? a : c;
- else
- return b > c ? b : c;
- }
- //用于维护信息
- int Maintaining(node *z)
- {
- if(z->inte.low > z->inte.high)
- return 0;
- return max(z->inte.high, z->left->max, z->right->max);
- }
- //搜索一个区间
- node *Interval_Search(Interval_Tree *T, interval i)
- {
- //从根结点开始
- node *x = T->root;
- //不是叶子且不重叠
- while(x != T->nil && !Overlap(i, x->inte))
- {
- //在左子树中
- if(x->left != T->nil && x->left->max >= i.low)
- x = x->left;
- //在右子树中
- else
- x = x->right;
- }
- return x;
- }
- //左旋,令y = x->right, 左旋是以x和y之间的链为支轴进行旋转
- //涉及到的结点包括:x,y,y->left,令node={p,l,r},具体变化如下:
- //x={x->p,x->left,y}变为{y,x->left,y->left}
- //y={x,y->left,y->right}变为{x->p,x,y->right}
- //y->left={y,y->left->left,y->left->right}变为{x,y->left->left,y->left->right}
- void Left_Rotate(Interval_Tree *T, node *x)
- {
- //令y = x->right
- node *y = x->right;
- //按照上面的方式修改三个结点的指针,注意修改指针的顺序
- x->right = y->left;
- if(y->left != T->nil)
- y->left->p = x;
- y->p = x->p;
- if(x->p == T->nil)//特殊情况:x是根结点
- T->root = y;
- else if(x == x->p->left)
- x->p->left = y;
- else
- x->p->right = y;
- y->left = x;
- x->p = y;
- //维护信息
- Maintaining(x);
- Maintaining(y);
- }
- //右旋,令y = x->left, 左旋是以x和y之间的链为支轴进行旋转
- //旋转过程与上文类似
- void Right_Rotate(Interval_Tree *T, node *x)
- {
- node *y = x->left;
- x->left = y->right;
- if(y->right != T->nil)
- y->right->p = x;
- y->p = x->p;
- if(x->p == T->nil)
- T->root = y;
- else if(x == x->p->right)
- x->p->right = y;
- else
- x->p->left = y;
- y->right = x;
- x->p = y;
- //维护信息
- Maintaining(x);
- Maintaining(y);
- }
- //红黑树调整
- void Interval_Tree_Insert_Fixup(Interval_Tree *T, node *z)
- {
- node *y;
- //唯一需要调整的情况,就是违反性质2的时候,如果不违反性质2,调整结束
- while(z->p->color == RED)
- {
- //p[z]是左孩子时,有三种情况
- if(z->p == z->p->p->left)
- {
- //令y是z的叔结点
- y = z->p->p->right;
- //第一种情况,z的叔叔y是红色的
- if(y->color == RED)
- {
- //将p[z]和y都着为黑色以解决z和p[z]都是红色的问题
- z->p->color = BLACK;
- y->color = BLACK;
- //将p[p[z]]着为红色以保持性质5
- z->p->p->color = RED;
- //把p[p[z]]当作新增的结点z来重复while循环
- z = z->p->p;
- }
- else
- {
- //第二种情况:z的叔叔是黑色的,且z是右孩子
- if(z == z->p->right)
- {
- //对p[z]左旋,转为第三种情况
- z = z->p;
- Left_Rotate(T, z);
- }
- //第三种情况:z的叔叔是黑色的,且z是左孩子
- //交换p[z]和p[p[z]]的颜色,并右旋
- z->p->color = BLACK;
- z->p->p->color = RED;
- Right_Rotate(T, z->p->p);
- }
- }
- //p[z]是右孩子时,有三种情况,与上面类似
- else if(z->p == z->p->p->right)
- {
- y = z->p->p->left;
- if(y->color == RED)
- {
- z->p->color = BLACK;
- y->color = BLACK;
- z->p->p->color = RED;
- z = z->p->p;
- }
- else
- {
- if(z == z->p->left)
- {
- z = z->p;
- Right_Rotate(T, z);
- }
- z->p->color = BLACK;
- z->p->p->color = RED;
- Left_Rotate(T, z->p->p);
- }
- }
- }
- //根结点置为黑色
- T->root->color = BLACK;
- }
- //插入一个结点
- void Interval_Tree_Insert(Interval_Tree *T, node *z)
- {
- node *y = T->nil, *x = T->root;
- //找到应该插入的位置,与二叉查找树的插入相同
- while(x != T->nil)
- {
- y = x;
- if(z->inte.low < x->inte.low)
- x = x->left;
- else
- x = x->right;
- }
- z->p = y;
- if(y == T->nil)
- T->root = z;
- else if(z->inte.low < y->inte.low)
- y->left = z;
- else
- y->right = z;
- z->left = T->nil;
- z->right = T->nil;
- //将新插入的结点转为红色
- z->color = RED;
- //从新插入的结点开始,向上调整
- Interval_Tree_Insert_Fixup(T, z);
- }
- //对树进行调整,x指向一个红黑结点,调整的过程是将额外的黑色沿树上移
- void Interval_Tree_Delete_Fixup(Interval_Tree *T, node *x)
- {
- node *w;
- //如果这个额外的黑色在一个根结点或一个红结点上,结点会吸收额外的黑色,成为一个黑色的结点
- while(x != T->root && x->color == BLACK)
- {
- //若x是其父的左结点(右结点的情况相对应)
- if(x == x->p->left)
- {
- //令w为x的兄弟,根据w的不同,分为三种情况来处理
- //执行删除操作前x肯定是没有兄弟的,执行删除操作后x肯定是有兄弟的
- w = x->p->right;
- //第一种情况:w是红色的
- if(w->color == RED)
- {
- //改变w和p[x]的颜色
- w->color = BLACK;
- x->p->color = RED;
- //对p[x]进行一次左旋
- Left_Rotate(T, x->p);
- //令w为x的新兄弟
- w = x->p->right;
- //转为2.3.4三种情况之一
- }
- //第二情况:w为黑色,w的两个孩子也都是黑色
- if(w->left->color == BLACK && w->right->color == BLACK)
- {
- //去掉w和x的黑色
- //w只有一层黑色,去掉变为红色,x有多余的一层黑色,去掉后恢复原来颜色
- w->color = RED;
- //在p[x]上补一层黑色
- x = x->p;
- //现在新x上有个额外的黑色,转入for循环继续处理
- }
- //第三种情况,w是黑色的,w->left是红色的,w->right是黑色的
- else
- {
- if(w->right->color == BLACK)
- {
- //改变w和left[x]的颜色
- w->left->color = BLACK;
- w->color = RED;
- //对w进行一次右旋
- Right_Rotate(T, w);
- //令w为x的新兄弟
- w = x->p->right;
- //此时转变为第四种情况
- }
- //第四种情况:w是黑色的,w->left是黑色的,w->right是红色的
- //修改w和p[x]的颜色
- w->color =x->p->color;
- x->p->color = BLACK;
- w->right->color = BLACK;
- //对p[x]进行一次左旋
- Left_Rotate(T, x->p);
- //此时调整已经结束,将x置为根结点是为了结束循环
- x = T->root;
- }
- }
- //若x是其父的左结点(右结点的情况相对应)
- else if(x == x->p->right)
- {
- //令w为x的兄弟,根据w的不同,分为三种情况来处理
- //执行删除操作前x肯定是没有兄弟的,执行删除操作后x肯定是有兄弟的
- w = x->p->left;
- //第一种情况:w是红色的
- if(w->color == RED)
- {
- //改变w和p[x]的颜色
- w->color = BLACK;
- x->p->color = RED;
- //对p[x]进行一次左旋
- Right_Rotate(T, x->p);
- //令w为x的新兄弟
- w = x->p->left;
- //转为2.3.4三种情况之一
- }
- //第二情况:w为黑色,w的两个孩子也都是黑色
- if(w->right->color == BLACK && w->left->color == BLACK)
- {
- //去掉w和x的黑色
- //w只有一层黑色,去掉变为红色,x有多余的一层黑色,去掉后恢复原来颜色
- w->color = RED;
- //在p[x]上补一层黑色
- x = x->p;
- //现在新x上有个额外的黑色,转入for循环继续处理
- }
- //第三种情况,w是黑色的,w->right是红色的,w->left是黑色的
- else
- {
- if(w->left->color == BLACK)
- {
- //改变w和right[x]的颜色
- w->right->color = BLACK;
- w->color = RED;
- //对w进行一次右旋
- Left_Rotate(T, w);
- //令w为x的新兄弟
- w = x->p->left;
- //此时转变为第四种情况
- }
- //第四种情况:w是黑色的,w->right是黑色的,w->left是红色的
- //修改w和p[x]的颜色
- w->color =x->p->color;
- x->p->color = BLACK;
- w->left->color = BLACK;
- //对p[x]进行一次左旋
- Right_Rotate(T, x->p);
- //此时调整已经结束,将x置为根结点是为了结束循环
- x = T->root;
- }
- }
- }
- //吸收了额外的黑色
- x->color = BLACK;
- }
- //找最小值
- node *Tree_Minimum(Interval_Tree *T, node *x)
- {
- //只要有比当前结点小的结点
- while(x->left != T->nil)
- x = x->left;
- return x;
- }
- //查找中序遍历下x结点的后继,后继是大于key[x]的最小的结点
- node *Tree_Successor(Interval_Tree *T, node *x)
- {
- //如果有右孩子
- if(x->right != T->nil)
- //右子树中的最小值
- return Tree_Minimum(T, x->right);
- //如果x的右子树为空且x有后继y,那么y是x的最低祖先结点,且y的左儿子也是
- node *y = x->p;
- while(y != NULL && x == y->right)
- {
- x = y;
- y = y->p;
- }
- return y;
- }
- //递归地查询二叉查找树
- node *Interval_Tree_Search(node *x, interval k)
- {
- //找到叶子结点了还没找到,或当前结点是所查找的结点
- if(x->inte.high < x->inte.low || k == x->inte)
- return x;
- //所查找的结点位于当前结点的左子树
- if(k.low < x->inte.low)
- return Interval_Tree_Search(x->left, k);
- //所查找的结点位于当前结点的左子树
- else
- return Interval_Tree_Search(x->right, k);
- }
- //红黑树的删除
- node *Interval_Tree_Delete(Interval_Tree *T, node *z)
- {
- //找到结点的位置并删除,这一部分与二叉查找树的删除相同
- node *x, *y;
- if(z->left == T->nil || z->right == T->nil)
- y = z;
- else y = Tree_Successor(T, z);
- if(y->left != T->nil)
- x = y->left;
- else x = y->right;
- x->p = y->p;
- if(y->p == T->nil)
- T->root = x;
- else if(y == y->p->left)
- y->p->left = x;
- else
- y->p->right = x;
- Maintaining(y->p);
- if(y != z)
- {
- z->inte = y->inte;
- Maintaining(z);
- }
- //如果被删除的结点是黑色的,则需要调整
- if(y->color == BLACK)
- Interval_Tree_Delete_Fixup(T, x);
- return y;
- }
- //求与z区间相交的区间的个数
- int Interval_Tree_Sum(node *r, interval z)
- {
- //叶子结点
- if(r->inte.low > r->inte.high)
- return 0;
- //不相交
- if(z.high < r->inte.low)
- return Interval_Tree_Sum(r->left, z);
- if(z.low > r->inte.high)
- return Interval_Tree_Sum(r->right, z);
- //相交
- int ret = 1;
- //不相交的部分继续求相交个数
- if(z.low < r->inte.low)
- {
- interval t(z.low, r->inte.low);
- ret = ret + Interval_Tree_Sum(r->left, t);
- }
- if(z.high > r->inte.high)
- {
- interval t(r->inte.high, z.high);
- ret = ret + Interval_Tree_Sum(r->right, t);
- }
- return ret;
- }
- /*
- 1 3 2 4
- 2 4 1 5
- */
- int main()
- {
- //生成一棵区间树
- Interval_Tree *T = new Interval_Tree;
- //记录所有的矩形
- Rectangle rect[N];
- //用于排序
- Sort_Node Sn[2*N];
- int i;
- for(i = 0; i < N; i++)
- {
- //输入矩形
- cin>>rect[i].x.low>>rect[i].x.high>>rect[i].y.low>>rect[i].y.high;
- //把x收集起来用于排序
- Sn[2*i] = Sort_Node(rect[i].x.low, i);
- Sn[2*i+1] = Sort_Node(rect[i].x.high, i);
- }
- //按照x值排序
- sort(Sn, Sn + 2*N, cmp);
- int ans = 0;
- //依次处理排序结果
- for(i = 0; i < 2 * N; i++)
- {
- int x = Sn[i].x;
- int id = Sn[i].id;
- //如果x是矩形i的左边x
- if(x == rect[id].x.low)
- {
- //求出区间树中与矩形i的y区间相交的区间个数
- ans = ans + Interval_Tree_Sum(T->root, rect[id].y);
- //把矩形i的y区间插入到区间树中
- node *z = new node(T->nil, rect[id].y);
- Interval_Tree_Insert(T, z);
- }
- //如果x是矩形i的右边x
- else if(x == rect[id].x.high)
- {
- //将矩形i的y区间从区间树中删除
- node *ret = Interval_Tree_Search(T->root, rect[id].y);
- if(ret != T->nil)
- Interval_Tree_Delete(T, ret);
- }
- }
- //输出结果
- cout<<ans<<endl;
- return 0;
- }