區間樹簡單實現(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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章