您的位置:首页 > 其它

算法导论 第14章 14.3 区间树

2014-11-07 19:44 218 查看


一、综述


1.区间树

区间树中一种对动态集合进行维护的红黑树,该集合中的每个元素x都包含一个区间int[x]


2.基础数据结构

红黑树,其中每个结点x包含一个区间域int[x],x的关键字为区间的低端点


3.附加信息

max[x]:以x为根的子树中所有区间的 端点的最大值


4.对信息的维护

max[x] = max(high[int[x]], max[left[x]], max[right[x]])


5.设计新的操作

INTERVAL-SEARCH(T, i):找出树T中覆盖区间i的那个结点。


二、代码


1.Interval_Tree.h

[cpp] view
plaincopy

#include <iostream>

using namespace std;

#define BLACK 0

#define RED 1

int Max(int a, int b, int c)

{

if(a > b)

return a > c ? a : c;

else

return b > c ? b : c;

}

//区间结构

struct interval

{

int low;

int high;

interval(int l, int h):low(l), high(h){}

};

//区间树结点结构

struct node

{

node *left;

node *right;

node *p;

int key;

bool color;

interval inte;

int max;

node(node *init, int l, int h):left(init),right(init),p(init),key(l),inte(l,h),max(h),color(BLACK){}

};

//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;

}

//区间树结构

class Interval_Tree

{

public:

node *root;//根结点

node *nil;//哨兵

/*构造函数*/

Interval_Tree(){nil = new node(NULL, -1, -1);root = nil;};

/*红黑树相关函数*/

void Left_Rotate(node *x);//左旋

void Right_Rotate(node *x);//右旋

void Insert_Fixup(node *z);//插入调整

void Insert(node *z);//插入

void Delete_Fixup(node *x);//删除调整

node *Delete(node *z);//删除

void Print();//输出

void Print(node *x);//输出

node *Search(node *x, int k);//在x的子树中查找关键字为k的结点

node *Tree_Successor(node *x);//求后继

node *Tree_Minimum(node *x);//求关键字最小的点

/*区间树相关函数*/

node *Interval_Search(interval i);

};

//左旋,令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 Interval_Tree::Left_Rotate(node *x)

{

//令y = x->right

node *y = x->right;

//按照上面的方式修改三个结点的指针,注意修改指针的顺序

x->right = y->left;

if(y->left != nil)

y->left->p = x;

y->p = x->p;

if(x->p == nil)//特殊情况:x是根结点

root = y;

else if(x == x->p->left)

x->p->left = y;

else

x->p->right = y;

y->left = x;

x->p = y;

//对附加信息的维护

y->max = x->max;

x->max = Max(x->inte.high, x->left->max, x->right->max);

}

//右旋,令y = x->left, 左旋是以x和y之间的链为支轴进行旋转

//旋转过程与上文类似

void Interval_Tree::Right_Rotate(node *x)

{

node *y = x->left;

x->left = y->right;

if(y->right != nil)

y->right->p = x;

y->p = x->p;

if(x->p == nil)

root = y;

else if(x == x->p->right)

x->p->right = y;

else

x->p->left = y;

y->right = x;

x->p = y;

//对附加信息的维护

y->max = x->max;

x->max = Max(x->inte.high, x->left->max, x->right->max);

}

//红黑树调整

void Interval_Tree::Insert_Fixup(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(z);

}

//第三种情况:z的叔叔是黑色的,且z是左孩子

//交换p[z]和p[p[z]]的颜色,并右旋

z->p->color = BLACK;

z->p->p->color = RED;

Right_Rotate(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(z);

}

z->p->color = BLACK;

z->p->p->color = RED;

Left_Rotate(z->p->p);

}

}

}

//根结点置为黑色

root->color = BLACK;

}

//插入一个结点

void Interval_Tree::Insert(node *z)

{

node *y = nil, *x = root;

//找到应该插入的位置,与二叉查找树的插入相同

while(x != nil)

{

x->max = max(x->max, z->max);

y = x;

if(z->key < x->key)

x = x->left;

else

x = x->right;

}

z->p = 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;

//从新插入的结点开始,向上调整

Insert_Fixup(z);

}

//对树进行调整,x指向一个红黑结点,调整的过程是将额外的黑色沿树上移

void Interval_Tree::Delete_Fixup(node *x)

{

node *w;

//如果这个额外的黑色在一个根结点或一个红结点上,结点会吸收额外的黑色,成为一个黑色的结点

while(x != 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(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(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(x->p);

//此时调整已经结束,将x置为根结点是为了结束循环

x = 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(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(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(x->p);

//此时调整已经结束,将x置为根结点是为了结束循环

x = root;

}

}

}

//吸收了额外的黑色

x->color = BLACK;

}

//找最小值

node *Interval_Tree::Tree_Minimum(node *x)

{

//只要有比当前结点小的结点

while(x->left != nil)

x = x->left;

return x;

}

//查找中序遍历下x结点的后继,后继是大于key[x]的最小的结点

node *Interval_Tree::Tree_Successor(node *x)

{

//如果有右孩子

if(x->right != nil)

//右子树中的最小值

return Tree_Minimum(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, int k)

{

//找到叶子结点了还没找到,或当前结点是所查找的结点

if(x->key == -1 || k == x->key)

return x;

//所查找的结点位于当前结点的左子树

if(k < x->key)

return Search(x->left, k);

//所查找的结点位于当前结点的左子树

else

return Search(x->right, k);

}

//红黑树的删除

node *Interval_Tree::Delete(node *z)

{

//找到结点的位置并删除,这一部分与二叉查找树的删除相同

node *x, *y, *p;

if(z->left == nil || z->right == nil)

{

y = z;

p = y->p;

p->max = p->inte.high;

p = p->p;

while(p->max == y->max)

{

p->max = Max(p->max, p->left->max, p->right->max);

p = p->p;

}

}

else y = Tree_Successor(z);

//对附加信息的维护

p = y->p;

p->max = p->inte.high;

p = p->p;

while(p->max == y->max)

{

p->max = Max(p->max, p->left->max, p->right->max);

p = p->p;

}

//删除y结点

if(y->left != nil)

x = y->left;

else x = y->right;

x->p = y->p;

if(y->p == nil)

root = x;

else if(y == y->p->left)

y->p->left = x;

else

y->p->right = x;

//替换

if(y != z)

{

z->inte = y->inte;

z->key = y->key;

z->max = y->max;

p = z->p;

while(p->max < z->max)

{

p->max = z->max;

p = p->p;

}

}

//如果被删除的结点是黑色的,则需要调整

if(y->color == BLACK)

Delete_Fixup(x);

return y;

}

void Interval_Tree::Print(node *x)

{

if(x->key == -1)

return;

Print(x->left);

cout<<x->inte.low<<' '<<x->inte.high<<endl;

Print(x->right);

}

void Interval_Tree::Print()

{

Print(root);

cout<<endl;

}

//搜索一个区间

node *Interval_Tree::Interval_Search(interval i)

{

//从根结点开始

node *x = root;

//不是叶子且不重叠

while(x != nil && !Overlap(i, x->inte))

{

//在左子树中

if(x->left != nil && x->left->max >= i.low)

x = x->left;

//在右子树中

else

x = x->right;

}

return x;

}

2.main.cpp

[cpp] view
plaincopy

#include <iostream>

#include "Interval_Tree.h"

using namespace std;

int main()

{

char ch;

int x, y;

//生成一棵顺序统计树

Interval_Tree *T = new Interval_Tree;

while(1)

{

cin>>ch;

switch(ch)

{

//插入一个元素

case 'I':

{

//cin>>x>>y;

x = rand() % 100;

y = rand() % 100;

if(x > y)swap(x, y);

node *z = new node(T->nil, x, y);

T->Insert(z);

break;

}

//删除一个元素

case 'D':

{

cin>>x;

node *z = T->Search(T->root, x);

if(z == NULL)

cout<<"not exist"<<endl;

else

T->Delete(z);

break;

}

//求第一个与[x,y]相交的区间

case 'S':

{

cin>>x>>y;

node *z = T->Interval_Search(interval(x, y));

if(z == NULL)

cout<<"not exist"<<endl;

else

cout<<z->inte.low<<' '<<z->inte.high<<endl;

break;

}

case 'P':

T->Print();

break;

default:

break;

}

}

return 0;

}


三、练习


14.3-1

[cpp] view
plaincopy

LEFT-ROTATE(T, x)

1 y <- right[x]

2 right[x] <- left[y]

3 if left[y] != nil[T]

4 then p[left[y]] <- x

5 p[y] <- p[x]

6 if p[x] = nil[T]

7 then root[T] <- y

8 else if x = left[p[x]]

9 then left[p[x]] <- y

10 else right[p[x]] <- y

11 left[y] <- x

12 p[x] <- y

13 max[y] <- max[x]

14 max[x] <- max(high[int[x]], max[left[x]], max[right[x]])


14.3-2

[cpp] view
plaincopy

对二中的程序做三点修改

(1)L37,<改成<=

(2)L40,>改成>=

(3)L443,>=改成>


14.3-3

那本答案PDF写得比较麻烦,不明天为什么要写得这么复杂,我只分了三种情况

[cpp] view
plaincopy

node* Interval_Tree::Search_Min(node *root, interval i)

{

node *x = root, *y;

//先从左子树上找

if(x->left && x->left->max >= i.low)

{

y = Search_Min(x->left, i);

if(y != nil)

return y;

}

//如果x与i相交,就不需要找左子树了

if(Overlap(x->inte, i))

return x;

//最后在右子树上找

if(x->right)

return Search_Min(x->right, i);

return nil;

}


14.3-4

[cpp] view
plaincopy

void Interval_Tree::Search_All(node *root, interval i)

{

node *x = root, *y;

//如果当前结点与i相交

if(Overlap(x->inte, i))

cout<<x->inte.low<<' '<<x->inte.high<<endl;

//先从左子树上找

if(x->left && x->left->max >= i.low)

Search_All(x->left, i);

//从右子树上找

if(x->right && x->key <= i.high)

Search_All(x->right, i);

}


14.3-5

[cpp] view
plaincopy

node* Interval_Tree::Search_Exactly(interval i)

{

//从根结点开始

node *x = root;

//不是叶子且不重叠

while(x != nil)

{

if(x->inte.low == i.low && x->inte.high == i.high)

return x;

//在左子树中

if(x->left != nil && x->key >= i.low)

x = x->left;

//在右子树中

else

x = x->right;

}

return nil;

}


14.3-6

算法导论-14.3-6-MIN-GAP


14.3-7

算法导论-14.3-7-O(nlgn)时间求矩形集合中重叠矩形的个数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: