线段树

线段树是描述单个或若干区间并的树形结构,属于平衡树的一种(平衡树是指左右子树的层数之差不超过1)。使用线段树要求知道所描述的区间端点可能取到的值。

线段树在一些acm题目中经常见到,这种数据结构主要应用在计算几何和地理信息系统中。下图就为一个线段树:

(注:可能你见过线段树的不同表示方式,但是都大同小异,根据自己的需要来建就行。)


对于线段树中的每一个非叶子节点[a,b],它的左孩子表示的区间为[a,(a+b)/2],右孩子表示的区间为[(a+b)/2,b]。因此线段树是平衡二叉树,最后的叶子节点数目为N,即整个线段区间的长度。

线段树是一棵二叉树,记为T(a, b),参数a,b表示区间[a,b],其中b-a称为区间的长度,记为L线段树T(a,b)也可递归定义为:

若L>1:

      [a, (a+b) div 2]为 T的左儿子;

      [(a+b) div 2,b]为T 的右儿子。

若L=1 :T为叶子节点。

 

线段树中的结点一般采取如下数据结构:

//定义线段树的结点
struct Node{
int left,right; //区间的左右值 
int count; //count=1表示结点所表示的区间被完全覆盖,count=0表示结点所表示的区间未被完全覆盖
Node *leftchild;
Node *rightchild; 
}; 


线段树的建立:

//建立线段树
Node *build(int k,int r) //k表示区间左值,r表示区间右值 
{
Node *root=new Node;
root->left=k;
root->right=r;
root->count=0;
root->leftchild=NULL;
root->rightchild=NULL;
if((k+1)<r)
{
int mid=(k+r)/2;
root->leftchild=build(k,mid);
root->rightchild=build(mid,r);
}
return root;


插入线段:

//插入算法
void Insert(Node *root,int a,int b)
{
int m;
if(root->count==0)
{
m=(root->right+root->left)/2;
if(a==root->left&&b==root->right)
root->count=1;
else if(b<=m)
Insert(root->leftchild,a,b);
else if(a>=m)
Insert(root->rightchild,a,b);
else if(a<m&&b>m)
{
Insert(root->leftchild,a,m);
Insert(root->rightchild,m,b);
}
}


统计被覆盖的线段段数:

//统计算法
int Count(Node *root)
{
int m,n;
if(root->count==1)
return (root->right-root->left);
else if((root->right-root->left)==1)
return 0;
m=Count(root->leftchild);
n=Count(root->rightchild);
return m+n;


线段树的应用:

引例:总线段从10000到60000,给出四个子线段:[10000,22000],[30300,55000],[44000,60000],[55000,60000],求解出这

个子线段的总长度,值得考虑的问题是这四个子线段有重叠的部分,也有断开的本分,而我们要求的是去除断开部分和去除重

叠部分的总长度。

完整代码如下:

#include<stdio.h>
//定义线段树的结点
struct Node{
int left,right; //区间的左右值 
int count; //count=1表示结点所表示的区间被完全覆盖,count=0表示结点所表示的区间未被完全覆盖
Node *leftchild;
Node *rightchild; 
}; 


//建立线段树
Node *build(int k,int r) //k表示区间左值,r表示区间右值 
{
Node *root=new Node;
root->left=k;
root->right=r;
root->count=0;
root->leftchild=NULL;
root->rightchild=NULL;
if((k+1)<r)
{
int mid=(k+r)/2;
root->leftchild=build(k,mid);
root->rightchild=build(mid,r);
}
return root;



//插入算法
void Insert(Node *root,int a,int b)
{
int m;
if(root->count==0)
{
m=(root->right+root->left)/2;
if(a==root->left&&b==root->right)
root->count=1;
else if(b<=m)
Insert(root->leftchild,a,b);
else if(a>=m)
Insert(root->rightchild,a,b);
else if(a<m&&b>m)
{
Insert(root->leftchild,a,m);
Insert(root->rightchild,m,b);
}
}



//统计算法
int Count(Node *root)
{
int m,n;
if(root->count==1)
return (root->right-root->left);
else if((root->right-root->left)==1)
return 0;
m=Count(root->leftchild);
n=Count(root->rightchild);
return m+n;



int main()
{
int qz,qy,n,i,j,x,y;
Node *p;
scanf("%d%d",&qz,&qy);
p=build(qz,qy);
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d%d",&x,&y);
Insert(p,x,y);
}
printf("%d\n",Count(p));
return 0;
}


运行结果截图:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值