线段树初学

本文深入探讨了线段树的构建、修改和查询方法,包括初始化、修改和查询操作的实现过程。通过实例演示了如何高效地在指定区间内进行数值存储、更新和检索,适合计算机科学领域学习和实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


线段树就是像下图的一种特殊的数,有存储,查询,修改等操作

需要用到

struct Node
{
int l,r;//存储边界值 
int dt;//存储该区间需改变的值的和 
int vis;//需要修改的值 
Node *leftchild;
Node *rightchild;
};

        线段树的操作主要分为两种:

        1,对单个区间存储数值,查询,修改

         2,对连续区间进行存储,查询,修改


         起始两种操作就是一个函数的区别,难点也是在这个函数上。第二种操作多了一个函数的使用,难度会大点

        线段数的使用需要用到三个函数,

        1,初始化    2,修改    3查询


              线段树的初始化就是以二分的思想建立一颗树,如需要建立区间(a,b)的线段树,先就是把(a,b) 二分为(a,c)

        (d,b)。   (a,c)作为(a,b)的左孩子, (d,b)作为(a,b)的右孩子,以此类推

       代码: 

void make_build(Node *cur,int l,int r)//初始化 
{
	cur->l=l; cur->r=r; cur->dt=0; cur->vis=0; 
	if (r!=l)
	{
		cur->leftchild=new Node;
		cur->rightchild=new Node;
		
		make_build(cur->leftchild ,l ,(l+r)/2);
		make_build(cur->rightchild ,(l+r)/2+1 ,r);
	}
	else 
        cur->leftchild=cur->rightchild=NULL;	
}
      

       接着就是修改,也是利用二分的思想,逐层去寻找符合要求的区间

       要注意的就是一个函数

       代码:     

void make_update(Node *cur)
{
	cur->leftchild->dt = cur->vis * (cur->leftchild->r-cur->leftchild->l+1);//向下传递
	cur->rightchild->dt = cur->vis * (cur->rightchild->r-cur->rightchild->l+1);//向下传递
	
	cur->leftchild->vis += cur->vis;//向下传递
        cur->rightchild->vis += cur->vis;//向下传递
	cur->vis=0;//标记,该层没有值向下传递 
}
       这个函数就像一个中转站,比如把(1,5)存入,我们的想法是当找到线段树中(1,5)区间就可以了,不要再继          续往下找,直到存到每一个叶子节点的区间去。但是我们要查找(1,2)时,我们会发现(1,2)区间并没有赋              值,这里就要用到void make_update(Node *cur)函数。当查找(1,2)的值时,他的作用就是把(1,5)区间的值        向下传递。

         

           查找函数其实就是把修改函数改下,没什么好讲的


       完整代码

       

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct Node
{
	int l,r;//存储边界值 
	int dt;//存储该区间需改变的值的和 
	int vis;//需要修改的值 
	Node *leftchild;
	Node *rightchild;
};

void make_build(Node *cur,int l,int r)//初始化 
{
	cur->l=l; cur->r=r; cur->dt=0; cur->vis=0; 
	if (r!=l)
	{
		cur->leftchild=new Node;
		cur->rightchild=new Node;
		
		make_build(cur->leftchild ,l ,(l+r)/2);
		make_build(cur->rightchild ,(l+r)/2+1 ,r);
	}
	else//忘记加 
        cur->leftchild=cur->rightchild=NULL;	
}
void make_update(Node *cur)
{
	cur->leftchild->dt = cur->vis * (cur->leftchild->r-cur->leftchild->l+1);
	cur->rightchild->dt = cur->vis * (cur->rightchild->r-cur->rightchild->l+1);
	
	cur->leftchild->vis += cur->vis;
        cur->rightchild->vis += cur->vis;
	cur->vis=0; 
}
void make_change(Node *cur,int l,int r,int date)//修改 
{
	int md;
	md=(cur->l+cur->r)/2;
	
	if (cur->l==l && cur->r==r)
	{
		cur->dt += date * (r-l+1);
		cur->vis += date; 
	}
	else
	{
		if (cur->vis!=0)
		{
			make_update(cur);
		}
		
		
		if (r<=md)
		{
			make_change(cur->leftchild,l,r,date);//改变的区间在cur的左儿子上
		}
		 
		
		if (l>md)
		{
			make_change(cur->rightchild,l,r,date);//改变的区间在cur的右儿子上
		}
		 
		
		if (l<=md && r>md)//改变的区间横跨左右儿子区间 
		{
			make_change(cur->leftchild,l,md,date);
			make_change(cur->rightchild,md+1,r,date);
		}
		cur->dt=cur->leftchild->dt+cur->rightchild->dt;//忘记加 
	}
}

int make_query(Node *cur,int l,int r)//查找 
{
	int md,as;
	as=0;
	md=(cur->l+cur->r)/2;
	 
	if (cur->l==l && cur->r==r)
	{
		return cur->dt;
	}
	else
	{
		if (cur->vis!=0)
		{
			make_update(cur);
		}
		
		
		if (r<=md)
		{
			as+=make_query(cur->leftchild,l,r);
		}
		
		if (l>md)
		{
			as+=make_query(cur->rightchild,l,r);
		}
		
		if (l<=md && r>md)
		{
			as+=make_query(cur->leftchild,l,md);
			as+=make_query(cur->rightchild,md+1,r);
		}
		return as;
	}
	//return as;放在外面 
}
int main()
{
	int n,m,p;
	int i,j,k;
	int a,b,c;
	Node A;
	A.l=0;  A.r=0;  A.dt=0;
	A.leftchild=NULL;
	A.rightchild=NULL;
	 
	while (~scanf ("%d",&n))
	{
		make_build(&A,1,n);
		p=1;
		while (p)
		{
			printf(".............................\n");
			printf("修改区间值,请输入 1 \n");
			printf("查寻区间值,请输入 2 \n");
			printf("结束请输入 0 \n");
			scanf ("%d",&k);
			if (k==1)
			{
				printf("输入区间a,b 和改变的值 c\n");
				scanf ("%d%d%d",&a,&b,&c);
				make_change(&A,a,b,c);
			}
			
			if (k==2)
			{
				m=0;
				printf("请输入要查询的区间a,b \n");
				scanf ("%d%d",&a,&b);
				m=make_query(&A,a,b);
				printf("%d\n",m);
			}
			
			if (k==0)
			p=0;
		}
		break;
	}
	return 0;
}

 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值