实验内容:
请利用区间树实现简易课表管理系统,支持:
1)插入一门新课;
2)删除课程;
3)查询特定时间区间的所有课程名称。注意:每门课信息包括课程编号、课程名称、上课时间。
源代码:
- LessonIntervalTree.h
#include <iostream>
#include <string>
using namespace std;
#define BLACK 0
#define RED 1
double Max(double a, double b, double c)
{
if(a > b)
return a > c ? a : c;
else
return b > c ? b : c;
}
/**************************************************区间、课程、课程区间树节点结构的定义*******************************************/
//区间结构
struct interval
{
double low;
double high;
interval(double l, double h):low(l), high(h){}
};
//课程结构
struct lesson
{
int number;
string name;
interval inter;
lesson(int n,string s,double l,double h ):number(n),name(s),inter(l,h){};
};
//课程区间树节点结构
struct node
{
node *left;
node *right;
node *p;
bool color;
double key;
interval inter;
double max;
lesson* thislesson;//课程的指针
node(node* init,double l,double h):left(init),right(init),p(init),color(BLACK),key(l),inter(l,h),max(h),thislesson(NULL){};
};
/************************************************判断是否重叠的函数**************************************************************/
//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 Lesson_Interval_Tree
{
public:
node *root;//根结点
node *nil;//哨兵
/*构造函数*/
Lesson_Interval_Tree(){nil = new node(NULL, -1.0, -1.0);root = nil;};
//红黑树相关函数
void Left_Rotate(node *x);//左旋
void Right_Rotate(node *x);//右旋
void Insert_Fixup(node *z);//红黑树插入调整
void Insert(node* z);//红黑树的插入+对节点附加信息的维护
node* Tree_Minimum(node *x);//找最小值
node* Tree_Successor(node *x);//查找中序遍历下x结点的后继,后继是大于key[x]的最小的结点
void Delete_Fixup(node *x);//删除调整/
node* Delete(node *z);//红黑树的删除+对节点附加信息的维护
//课程区间树相关函数
node *Interval_Search(interval i);//区间搜索
void Lesson_Interval_Insert(node *z);//插入一门课程
node* Lesson_Interval_Delete(node *z);//删除一门课程
void Print_Node(node *z);//中序打印以该节点为根的课程区间树
void Print_Tree();//中序打印该课程区间树
node* Lesson_Interval_Search(interval i);//搜索与特定时间区间重叠的一门课程并打印
void Search_All(node* root,interval i);//搜索与该区间重叠的所有区间并打印
};
//课程区间树的函数
//左旋+对节点附加信息的维护
void Lesson_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->inter.high, x->left->max, x->right->max);
}
//右旋+对节点附加信息的维护
void Lesson_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->inter.high, x->left->max, x->right->max);
}
//红黑树插入调整
void Lesson_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 Lesson_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);
}
//找最小值
node *Lesson_Interval_Tree::Tree_Minimum(node *x)
{
//只要有比当前结点小的结点
while(x->left != nil)
x = x->left;
return x;
}
//查找中序遍历下x结点的后继,后继是大于key[x]的最小的结点
node *Lesson_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 != nil && x == y->right)
{
x = y;
y = y->p;
}
return y;
}
//对树进行调整,x指向一个红黑结点,调整的过程是将额外的黑色沿树上移
void Lesson_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* Lesson_Interval_Tree::Delete(node *z)
{
//找到结点的位置并删除,这一部分与二叉查找树的删除相同
node *x, *y, *p;
if(z->left == nil || z->right == nil)
{
y = z;
//以下是对节点附加信息的维护部分
p = y->p;
if(y->left!=nil)
{
p->max=max(p->inter.high,y->left->max);
}
else
{
p->max=max(p->inter.high,y->right->max);
}
p = p->p;
while(p->max == y->max)
{
p->max = Max(p->inter.high, p->left->max, p->right->max);
p = p->p;
}
}
else y = Tree_Successor(z);
//对附加信息的维护
p = y->p;
if(y->left!=nil)
{
p->max=max(p->inter.high,y->left->max);
}
else
{
p->max=max(p->inter.high,y->right->max);
}
p = p->p;
while(p->max == y->max)
{
p->max = Max(p->inter.high, 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->inter = y->inter;
z->key = y->key;
z->max = y->max;
z->thislesson=y->thislesson;
p = z->p;
while(p->max < z->max&&p!=nil)
{
p->max = z->max;
p = p->p;
}
}
//如果被删除的结点是黑色的,则需要调整
if(y->color == BLACK)
Delete_Fixup(x);
return y;
}
//搜索一个区间
node *Lesson_Interval_Tree::Interval_Search(interval i)
{
//从根结点开始
node *x = root;
//不是叶子且不重叠
while(x != nil && !Overlap(i, x->inter))
{
//在左子树中
if(x->left != nil && x->left->max >= i.low)
x = x->left;
//在右子树中
else
x = x->right;
}
return x;
}
//插入一门课程
void Lesson_Interval_Tree::Lesson_Interval_Insert(node *z)
{
Insert(z);
}
//删除一门课程
node* Lesson_Interval_Tree::Lesson_Interval_Delete(node *z)
{
node* y = Interval_Search(z->inter);
node* m=new node(NULL,-1,-1);
if(y == nil)
{
cout<<"该课程不存在,请确认信息"<<endl;
}
else
{
m=Delete(y);
}
return m;
}
//打印以该节点为根的树
void Lesson_Interval_Tree::Print_Node(node *z)
{
if(z == nil)
return;
Print_Node(z->left);
cout<<'\t'<<"时间区间"
<<'\t'<<'['<<z->inter.low
<<','<<z->inter.high<<']'
<<'\t'<<"课程编号"
<<'\t'<<z->thislesson->number
<<'\t'<<"课程名"
<<'\t'<<z->thislesson->name
<<endl;
Print_Node(z->right);
}
//打印一棵树
void Lesson_Interval_Tree::Print_Tree()
{
Print_Node(root);
cout<<endl;
}
//搜索与特定时间区间重叠的一门课程并打印
node* Lesson_Interval_Tree::Lesson_Interval_Search(interval i)
{
node* z=Interval_Search(i);
cout<<'\t'<<"时间区间"
<<'\t'<<'['<<z->inter.low
<<','<<z->inter.high<<']'
<<'\t'<<"课程编号"
<<'\t'<<z->thislesson->number
<<'\t'<<"课程名"
<<'\t'<<z->thislesson->name
<<endl;
return z;
}
//搜索与某区间重叠的所有课程并打印
void Lesson_Interval_Tree::Search_All(node* root,interval i)
{
node *x = root, *y;
//如果当前结点与i相交
if(Overlap(x->inter, i))
cout<<'\t'<<"时间区间"
<<'\t'<<'['<<x->inter.low
<<','<<x->inter.high<<']'
<<'\t'<<"课程编号"
<<'\t'<<x->thislesson->number
<<'\t'<<"课程名"
<<'\t'<<x->thislesson->name
<<endl;
//先从左子树上找
if(x->left != nil && x->left->max >= i.low)
Search_All(x->left, i);
//从右子树上找
if(x->right != nil && x->key <= i.high)
Search_All(x->right, i);
}
2.main.cpp
#include <iostream>
#include "LessonIntervalTree.h"
using namespace std;
int main()
{
char ch;
int lesson_number;
string lesson_name;
double l;
double h;
Lesson_Interval_Tree* T=new Lesson_Interval_Tree;
while(1)
{
cout<<"请选择插入,删除,或者查询与输入时间区间重叠的所有课程,或者中序打印整棵树,输入I(插入),D(删除),或S(查询),或P(打印)"<<endl;
cin>>ch;
switch(ch)
{
case 'I':
{
cout<<"输入课程编号"<<endl;
cin>>lesson_number;
cout<<"输入课程名(英文)"<<endl;
cin>>lesson_name;
cout<<"输入时间区间,中间以空格隔开"<<endl;
cin>>l>>h;
node* n=new node(T->nil,l,h);
lesson *one_lesson=new lesson(lesson_number,lesson_name,l,h);
n->thislesson=one_lesson;
T->Lesson_Interval_Insert(n);
break;
}
case 'D':
{
cout<<"输入时间区间,中间以空格隔开"<<endl;
cin>>l>>h;
node* n=new node(T->nil,l,h);
T->Lesson_Interval_Delete(n);
break;
}
case 'S':
{
cout<<"输入时间区间,中间以空格隔开"<<endl;
cin>>l>>h;
node* n=new node(T->nil,l,h);
T->Search_All(T->root,n->inter);
break;
}
case 'P':
{
T->Print_Tree();
break;
}
default:
break;
}
}
return 0;
}