区间树简单实现(interval_tree)

interval_tree.cpp

/* 
区间树(interval tree)是一种对动态集合进行维护的扩张红黑树,因此可在实验二红黑树的基础上进行扩张。
*/  

#include <iostream>
#include <deque>
#include <iomanip>
#include <sstream>
#include <time.h>
#include <sys/time.h>

#define SENTINEL -100000    //哨兵,作为nil结点的key,方便树状输出时临界点判断
using namespace std;
#define MAX_TREE_NODE	30000

enum Color{RED=0,BLACK=1};  //定义枚举类型,即红黑树结点颜色类型,0表示红色,1表示黑色

typedef struct Interval     //定义一个表示区间范围的结构体
{
    int low;                //区间的低端点(low endpoint)
    int high;               //区间的高端点(high endpoint)
}Interval;

typedef struct Node        //声明红黑树结点
{
    Color color;           //红黑树结点的颜色类型,
    struct Node *parent;   //父节点
    struct Node *left;     //左孩子
    struct Node *right;    //右孩子
    Interval interval;     //区间
    int max;               //附加信息,记录以该节点为根的子树中所有区间端点的最大值
}Node;

typedef struct RBTree      //定义一个红黑树
{
    Node *root;            //根节点
    Node *nil;             //哨兵结点,避免讨论结点的边界情况
}RBTree;

//选择显示
void Display()             
{
    cout<<"************************************************************************\n";
    cout<<"****           请选择您要的红黑树操作!!                           ****\n";
    cout<<"****   I:插入区间    D:区间树当前状态   S:查找区间    E:退出    ****\n";
    cout<<"****   Insert         Display             Search         Exit       ****\n";
    cout<<"************************************************************************\n";
}

//求三个参数中的最大值
int GetMax(int high,int leftMax,int rightMax)     
{
    int temp=(leftMax>rightMax)?leftMax:rightMax;
    return (high>temp)?high:temp;
}

//左旋,结点x原来的右子树y旋转成x的父母
void LeftRotate(RBTree * rbTree,Node *x)
{
    if(x->right!=rbTree->nil)
    {
        Node *y=x->right;
        x->right=y->left;
        if(y->left!=rbTree->nil)
        {
            y->left->parent=x;
        }
        y->parent=x->parent;
        if(x->parent==rbTree->nil)    //空树,将y设为根
        {
            rbTree->root=y;
        }
        else if(x==x->parent->left)   //x为左子树,将y放在x父节点的左子树
        {
            x->parent->left=y;
        }
        else
        {
            x->parent->right=y;
        }
        y->left=x;
        x->parent=y;

        //以下为区间树与红黑树左旋调整的差异,即要调整结点max的大小,
        //且必须先计算x的max,在计算y的max
        x->max=GetMax(x->interval.high,x->left->max,x->right->max);
        y->max=GetMax(y->interval.high,y->left->max,y->right->max);
    }
    else
    {
        cout<<"Error: can't left rotate,because no rigth child!"<<endl;
    }
}

//右旋,结点x原来的左子树y旋转成x的父母
void RightRotate(RBTree * rbTree,Node *x)
{
    if(x->left!=rbTree->nil)
    {
        Node *y=x->left;
        x->left=y->right;
        if(y->right!=rbTree->nil)
        {
            y->right->parent=x;
        }

        y->parent=x->parent;
        if(x->parent==rbTree->nil)   
        {
            rbTree->root=y;
        }
        else if(x==x->parent->left)  
        {
            x->parent->left=y;
        }
        else
        {
            x->parent->right=y;
        }
        y->right=x;
        x->parent=y;

        //以下为区间树与红黑树左旋调整的差异,即要调整结点max的大小
        //且必须先计算x的max,在计算y的max
        x->max=GetMax(x->interval.high,x->left->max,x->right->max);
        y->max=GetMax(y->interval.high,y->left->max,y->right->max);
    }
    else
    {
        cout<<"Error: can't right rotate,because no left child!"<<endl;
    }

}

//插入结点
void RBInsert(RBTree *rbTree,Interval interval)
{
    void RBInsertFixUp(RBTree *rbTree,Node *z); 
    if(rbTree->root==NULL)
    {//当根为空时,单独处理,直接插入到根结点中
        rbTree->root=new Node;
        rbTree->nil=new Node;

        rbTree->root->left=rbTree->nil;
        rbTree->root->right=rbTree->nil;
        rbTree->root->parent=rbTree->nil;
        rbTree->root->interval.low=interval.low;      //设置区间低端点
        rbTree->root->interval.high=interval.high;    //设置区间高端点
        rbTree->root->max=interval.high;      //初始根的max设为自己的high

        rbTree->root->color=BLACK;            //根节点color设为黑

        rbTree->nil->parent=rbTree->root;
        rbTree->nil->left=rbTree->root;
        rbTree->nil->right=rbTree->root;
        rbTree->nil->interval.low=rbTree->nil->interval.high=SENTINEL;  //将nil的区间设为哨兵
        rbTree->nil->color=BLACK;     //nil结color也设为黑
        rbTree->root->max=0;          //nil节点的max设为0,便于其他节点max的维护

    }
    else
    {//如果树不为空,那么从根节点开始,从上往下查找插入点
        Node *y=rbTree->nil;     //y用于当前扫描结点x的父节点
        Node *x=rbTree->root;    //从根节点开始扫描
        while(x!=rbTree->nil)    //查找插入位置,以低端点为排序键值
        {
            if(interval.low==x->interval.low && interval.high < x->interval.high)
            {
                cout<<"键值重复,请输入不同的键值!!"<<endl;
                return;
            }
            y=x;
            x=interval.low<x->interval.low ? x->left : x->right;  
        }
        Node *z=new Node;       //new一个Node结点空间
        z->color=RED;           //新插入的color设为红色
        z->interval.low=interval.low;
        z->interval.high=interval.high;
        z->left=z->right=rbTree->nil;
        z->max=GetMax(interval.high,z->left->max,z->right->max);
        z->parent=y;
        if(interval.low<y->interval.low)
            y->left=z;
        else
            y->right=z;

        RBInsertFixUp(rbTree,z);   //插入后对树进行调整
    }
}

//插入后调整树,以维持红黑树的5条性质
void RBInsertFixUp(RBTree *rbTree,Node *z)   
{
    Node *y;      //用于记录z的叔叔结点
    while(z->parent->color==RED)   //因为插入的结点是红色的,所以只可能违背性质4,即假如父结点也是红色的,要做调整 
    {
        if(z->parent->parent->left==z->parent)  //如果要插入的结点z是其父结点的左子树
        {
            y=z->parent->parent->right;         // y设置为z的叔父结点
            if(y->color==RED)                   //case 1: y的颜色为红色
            {
                z->parent->parent->color=RED;
                y->color=BLACK;
                z->parent->color=BLACK;
                z=z->parent->parent;
            }
            else
            {
                if(z==z->parent->right)    //case 2: y的颜色为黑色,并且z是z的父母的右结点,则z左旋转
                {
                    z=z->parent;
                    LeftRotate(rbTree,z);
                }
                z->parent->parent->color=RED;     //case 3: 如果y的颜色为黑色,并且z是z的父母的左结点
                z->parent->color=BLACK;
                RightRotate(rbTree,z->parent->parent);
            }
        }
        else    //与前一种情况对称,要插入的结点z是其父结点的右子树,注释略去 
        {
            y=z->parent->parent->left;
            if(y->color==RED)
            {
                z->parent->parent->color=RED;
                y->color=BLACK;
                z->parent->color=BLACK;
                z=z->parent->parent;
            }
            else
            {
                if(z->parent->left==z)
                {
                    z=z->parent;
                    RightRotate(rbTree,z);
                }
                z->parent->parent->color=RED;
                z->parent->color=BLACK;
                LeftRotate(rbTree,z->parent->parent);
            }
        }
    }
    rbTree->root->color=BLACK;   //最后如果上升为rbTree的根的话,把根的颜色设为黑色

}

//查找与给定区间重叠的区间
Node* IntervalSearch(RBTree * rbTree,Interval interval)
{
    Node *x=rbTree->root;    //从根开始查找
    while(x!=rbTree->nil&&!(interval.low<=x->interval.high&&interval.high>=x->interval.low))
    {//若x不等于nil节点且x与interval不重叠,则进行判断
        if(x->left!=rbTree->nil&&x->left->max>=interval.low)
            x=x->left;       //到x的左子树中继续查找
        else
            x=x->right;      //左子树必查不到,到右子树查
    }
    return x;    //x=nil或者x与interval重叠
}

递归在z的左右子树中查找与interval重叠的具有最小低端点的区间
//Node* IntervalSearchMin(RBTree* rbTree,Node* z,Interval interval)  
//{  
//    Node *x = z, *y;    
//    //先从左子树上找  
//    if(x->left && x->left->max >= interval.low)  
//    {  
//        y = IntervalSearchMin(rbTree,x->left, interval);  
//        if(y != rbTree->nil)  
//            return y;  
//    }  
//    //如果x与i相交,就不需要找左子树了  
//    if(interval.low<=x->interval.high&&interval.high>=x->interval.low)  
//        return x;  
//    //最后在右子树上找  
//    if(x->right)  
//        return IntervalSearchMin(rbTree,x->right, interval);  
//    //如果找不到,返回nil
//    return rbTree->nil;     
//}  

//求树高
//int MaxHeight(Node * root,Node *nil)
//{
//    if(root==nil)return 0;
//    int leftHeight=MaxHeight(root->left,nil);
//    int rightHeight=MaxHeight(root->right,nil);
//    return (leftHeight>rightHeight)?leftHeight+1:rightHeight+1;
//}

// convert an integer value to string
//string IntToString(int val) 
//{
//  ostringstream ss;
//  ss << val;
//  return ss.str();
//}

// Print the arm branches (eg, /    \ ) on a line
//void PrintBranches(int branchLen, int nodeSpaceLen, int startLen, int nodesInThisLevel, const deque<Node*>& nodesQueue) 
//{
//  deque<Node*>::const_iterator iter = nodesQueue.begin();
//  for (int i = 0; i < nodesInThisLevel / 2; i++) 
//  {
//    cout << ((i == 0) ? setw(startLen-1) : setw(nodeSpaceLen-2)) << "" << ((*iter&&(*iter)->interval.low!=SENTINEL) ? "/" : " ");
//    *iter++;
//    cout << setw(2*(branchLen+1)+7) << "" << ((*iter&&(*iter)->interval.low!=SENTINEL) ? "\\" : " ");
//    //setw(2*branchLen+4)改为setw(2*branchLen+5)
//    *iter++;
//  }
//  cout << endl;
//}

 Print the branches and node (eg, ___10___ )
//void PrintNodes(int branchLen, int nodeSpaceLen, int startLen, int nodesInThisLevel, const deque<Node*>& nodesQueue)
//{
//  deque<Node*>::const_iterator iter = nodesQueue.begin();
//  for (int i = 0; i < nodesInThisLevel; i++, iter++)
//  {
//    cout << ((i == 0) ? setw(startLen) : setw(nodeSpaceLen)) << "" << ((*iter && (*iter)->interval.low!=SENTINEL && (*iter)->left->interval.low!=SENTINEL ) ? setfill('_') : setfill(' '));
//    if(*iter&&(*iter)->interval.low!=SENTINEL)
//    {
//        cout<<setw(branchLen+2)<<"["<<IntToString((*iter)->interval.low)<<","<<IntToString((*iter)->interval.high)<<"]";
//        //cout<<(((*iter)->color==RED) ? "红":"黑");
//    }
//    else
//    {
//        cout << setw(branchLen+1)<<"";
//        //cout << setw(branchLen+2)<<"";
//    }
//    cout << ((*iter && (*iter)->interval.low!=SENTINEL && (*iter)->right->interval.low!=SENTINEL ) ? setfill('_') : setfill(' ')) << setw(branchLen+1) << "" << setfill(' ');
//    //setw(branchLen)改为setw(branchLen+1)
//  }
//  cout << endl;
//}
//
在控制台树形输出红黑树
//void PrintPretty(RBTree * tree, int level, int indentSpace) 
//{
//    int h = MaxHeight(tree->root,tree->nil);
//    int nodesInThisLevel = 1;
//
//    int branchLen = 2*((int)pow(2.0,h)-1) - (3-level)*(int)pow(2.0,h-1);  
//    int nodeSpaceLen = 5 + (level+1)*(int)pow(2.0,h);  
//    int startLen = branchLen + (3-level) + indentSpace; 
//
//    deque<Node*> nodesQueue;
//    nodesQueue.push_back(tree->root);
//    for (int r = 1; r <= h; r++) {
//        PrintBranches(branchLen, nodeSpaceLen, startLen, nodesInThisLevel, nodesQueue);
//        branchLen = branchLen/2 - 1;
//        nodeSpaceLen = nodeSpaceLen/2 + 1;
//        startLen = branchLen + (3-level) + indentSpace;
//        PrintNodes(branchLen, nodeSpaceLen, startLen, nodesInThisLevel, nodesQueue);
//
//        for (int i = 0; i < nodesInThisLevel; i++) {
//            Node *currNode = nodesQueue.front();
//            nodesQueue.pop_front();
//            if (currNode&&currNode!=tree->nil) {
//                nodesQueue.push_back(currNode->left);
//                nodesQueue.push_back(currNode->right);
//            } else {
//                nodesQueue.push_back(NULL);
//                nodesQueue.push_back(NULL);
//            }
//        }
//        nodesInThisLevel *= 2;
//    }
//}

int main()
{
	struct timeval tv;

    RBTree tree;
    tree.root=tree.nil=NULL;
    //char choose;
    Interval interval[MAX_TREE_NODE];
	time_t t;
	int A[MAX_TREE_NODE],i,temp;

	srand((unsigned)time(&t));
	for(i = 0;i < MAX_TREE_NODE;i++)
	{
		A[i] = rand() % 20;
	}
	for(i=0;i<MAX_TREE_NODE;i++)
	{
		interval[i].low = A[i];
		interval[i].high = A[i]+5;
	}
	gettimeofday(&tv, NULL);
	printf("%d\t%d\n", tv.tv_usec, tv.tv_sec);
	for(i=0;i<MAX_TREE_NODE;i++)
	{
		RBInsert(&tree,interval[i]);
	}

	temp=rand()%20;
	printf("search:[%d,%d]\n",temp,temp);
	Interval tmp;
	tmp.high = temp;
	tmp.low  = temp;
	gettimeofday(&tv, NULL);
	printf("%d\t%d\n", tv.tv_usec, tv.tv_sec);
	Node* x = IntervalSearch(&tree,tmp);
	if(x!=tree.nil)
		printf("OK:x=[%d,%d]\n",x->interval.low,x->interval.high);
	else printf("NOK\n");
	gettimeofday(&tv, NULL);
	printf("%d\t%d\n", tv.tv_usec, tv.tv_sec);

#if 0
    while(true)
    {
        Display();
        cin>>choose;
        switch(choose)
        {
            case 'E':exit(0);break;   //选择0则跳出系统
            case 'I':     
                {//选择1则插入结点
                    cout<<"请输入区间的左右两个端点,中间以空格隔开:";
                    cin>>interval.low>>interval.high;
                    RBInsert(&tree,interval);
                    cout<<endl;
                    break;
                }
            //case 'D':   
            //    {//选择2则在控制台以树的形式显示红黑树
            //        cout<<endl<<"此时红黑树状态如下:"<<endl;
            //        PrintPretty(&tree,2,3);
            //        cout<<endl;
            //        break;
            //    }
            case 'S':
                {//显示查找重叠区间的结果
                    cout<<"请输入待查找的区间:";
                    cin>>interval.low>>interval.high;
                    Node* x=IntervalSearch(&tree,interval);
                    if(x!=tree.nil)
                    {
                        //Node* min_x=IntervalSearchMin(&tree,tree.root,interval);
                        cout<<"Output:最先找到的重叠区间为:"<<"["<<x->interval.low<<","<<x->interval.high<<"]"<<endl;
                        //cout<<"       具有最小低端点的重叠区间为:"<<"["<<min_x->interval.low<<","<<min_x->interval.high<<"]"<<endl<<endl;
                    }
                    else
                        cout<<"Output:无重叠区间!"<<endl<<endl;
                }            
        }
    }
#endif
	return 0;
}

[root@gnos-x64-dev ~]# g++ -g interval_tree.cpp -o interval_tree
[root@gnos-x64-dev ~]# ./interval_tree                          
813816  1475997819
search:[12,12]
824964  1475997819
OK:x=[11,16]
824967  1475997819
[root@gnos-x64-dev ~]# expr 824964 - 813816
11148
[root@gnos-x64-dev ~]# expr 824967 - 824964
3
[root@gnos-x64-dev ~]# 

插入时间11148us

查找时间3us


[root@gnos-x64-dev ~]# g++ -pg interval_tree.cpp -o interval_tree
[root@gnos-x64-dev ~]# gprof interval_tree gmon.out -p           
Flat profile:


Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ns/call  ns/call  name    
100.32      0.01     0.01    30000   334.41   334.41  RBInsert(RBTree*, Interval)
  0.00      0.01     0.00    95479     0.00     0.00  GetMax(int, int, int)
  0.00      0.01     0.00    29999     0.00     0.00  RBInsertFixUp(RBTree*, Node*)
  0.00      0.01     0.00    29652     0.00     0.00  LeftRotate(RBTree*, Node*)
  0.00      0.01     0.00     3088     0.00     0.00  RightRotate(RBTree*, Node*)
  0.00      0.01     0.00        1     0.00     0.00  global constructors keyed to _Z7Displayv
  0.00      0.01     0.00        1     0.00     0.00  IntervalSearch(RBTree*, Interval)
  0.00      0.01     0.00        1     0.00     0.00  __static_initialization_and_destruction_0(int, int)


 %         the percentage of the total running time of the
time       program used by this function.


cumulative a running sum of the number of seconds accounted
 seconds   for by this function and those listed above it.


 self      the number of seconds accounted for by this
seconds    function alone.  This is the major sort for this
           listing.


calls      the number of times this function was invoked, if
           this function is profiled, else blank.
 
 self      the average number of milliseconds spent in this
ms/call    function per call, if this function is profiled,
           else blank.


 total     the average number of milliseconds spent in this
ms/call    function and its descendents per call, if this 
           function is profiled, else blank.


name       the name of the function.  This is the minor sort
           for this listing. The index shows the location of
           the function in the gprof listing. If the index is
           in parenthesis it shows where it would appear in
           the gprof listing if it were to be printed.


http://hujw0710.blog.163.com/blog/static/8797282200952324755785/

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值