算法导论上机实验3——用区间树实现简易课程管理系统,包括课程的插入、删除和查找

实验内容:
请利用区间树实现简易课表管理系统,支持:
1)插入一门新课;
2)删除课程;
3)查询特定时间区间的所有课程名称。注意:每门课信息包括课程编号、课程名称、上课时间。
源代码:

  1. 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;
}


    原文作者:查找算法
    原文地址: https://blog.csdn.net/u012375442/article/details/49999181
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞