范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

重要算法线段树入门

  部分图片是从网络获得,侵删!
  熊猫人线段树什么是线段树
  入门算法的同学一定对数据结构的线段树不陌生,线段树也是算法比赛常考且非常重要的一种高效解决区间的方法。线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 下面我会对线段树的基本方法展开详细的介绍。线段树的概念
  线段树是一种二叉搜索树,什么叫做二叉搜索树,首先满足二叉树,每个结点度小于等于二,即每个结点最多有两颗子树,何为搜索,我们要知道,线段树的每个结点都存储了一个区间,也可以理解成一个线段,而搜索,就是在这些线段上进行搜索操作得到你想要的答案。
  建立一个线段树的时间复杂度是O(N)(不是O(nlogn)!!)使用线段树可以快速地查找某一个节点在若干条线段中出现的次数,其查找的时间复杂度为O(logN)。而未优化的空间复杂度为O(2N),因此有时需要离散化让空间压缩。
  线段树采用分治思想(主要是二分法, 一般可以高效率地解决区间问题。线段树的叶子节点是最简单的节点,叶子节点的父节点是两个叶子节点的区间的并集。对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的节点数目为N,即整个线段区间的长度。区间[1-5]的线段树如下图所示。
  1到5区间的线段树线段树使用场景
  上文已经提到线段树主要是可以高效地解决区间问题,那么具体过程是怎么样的呢?
  大家可以先看看这个经典选段树的题,后续我会把题解发到推文上 。
  链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166(无法在内容中引用外文网址 链接我会放在下面)
  我们打开后我们会发现,这个题目是典型的区间处理题目,但是数据范围极大而且还要处理各种操作,直接用数组存储并遍历操作会直接超时。这时线段树(快且方便)就派上用场了。线段树主要处理极大范围的区间操作。线段树主要代码操作及其讲解线段树的定义
  父节点val值是所有子节点值的和,这样有利于下面的操作,叶子节点的值就是区间内输入的值。struct Nd //定义线段树节点  { 	int left;//左孩子  	int right;//右孩子  	int val;//该节点值  };
  2.建树
  利用递归建立树是常用操作,当r==l时表明到达叶子节点。最后arr[i].val = arr[i<<1].val+arr[i<<1|1].val;是递归回溯时完成子节点对父节点的赋值,因此父节点val值是所有子节点值的和。x<<1与x<<1|1相当于x*=2和x=x*2+1,分别寻找两个子节点。详细如下图所示。
  父节点对应子节点void build(int l,int r,int i)//利用递归构成线段树  { 	arr[i].left=l;//将左右区间的值赋值给节点  	arr[i].right=r; 	if(r==l) 	{ 		std::cin >> arr[i].val;//最后叶子节点才存储要存储的值 		return ; 	}  	build(l,(l+r)/2,i<<1);//递归到左节点  	build((r+l)/2+1,r,i<<1|1); //递归到右节点  	arr[i].val = arr[i<<1].val+arr[i<<1|1].val;//递归回溯时完成子节点对父节点的赋值  }
  arr[i].val = arr[i<<1].val+arr[i<<1|1].val;完成时的值是下面所有节点(有亲缘关系)的和,递归是从深到浅,从左到右(左右可变)遍历的过程(DFS就是如此)。
  3.DFS打印树
  这个是方便看树每一个节点以及是否健全,找bug用~void dfs_print(int l,int r,int i) { 	/*if(r==l) 	{ 		std::cout << arr[i].val << " ";//打印叶子节点  		return ; 	}*/ 	std::cout << arr[i].val << " "; 	if(r==l) 	{ 		return ; 	} 	dfs_print(l,(l+r)/2,i<<1); 	dfs_print((r+l)/2+1,r,i<<1|1);  }
  4.更新节点
  arr[d].left==arr[d].right时就是叶子,别忘了对父节点回溯时的刷新值。递归找叶节点后更改,void update(int d,int r,int b)// d为当前节点 r为更新的节点 b为改变节点的值 这个递归算法实际就是二分查找  { 	if(arr[d].left==arr[d].right)//递归到叶子节点时 肯定下标是r 注意只能更改叶子节点  	{ 		arr[d].val=b;//更改值  		return; 	}	 	int mid = (arr[d].left+arr[d].right)>>1; 	if(r>mid)update(d<<1|1,r,b);//递归到右son 	else 	update(d<<1,r,b);//递归到左son 	arr[d].val = arr[d<<1].val+arr[d<<1|1].val;//递归回溯时完成子节点对父节点的赋值  }
  5.区间查询(返回区间内所有节点和)
  将范围一步一步划分小,充分利用父节点的值增加效率。//区间查询 我们不要忘记,父节点可是存储着它俩儿子值的和  int query(int d,int l,int r)//这个是返回区间的和  { 	int sum = 0;//区间总和 	if(l==arr[d].left&&r==arr[d].right)//1.如果在目的范围内的父亲节点 可以增加效率 		sum += arr[d].val; 	else{ 		int mid = ((arr[d].left+arr[d].right)/2); 		if(l<=mid)		 			(r>mid)?sum+=query(d<<1,l,mid):sum+=query(d<<1,l,r); 		if(r>mid) 			(l<=mid)?sum+=query(d<<1|1,mid+1,r):sum+=query(d<<1|1,l,r); 	} 	return sum; }
  父节点存储所有子节点的总值,所以我们只需查找范围内的父节点即可。可能您不明白9到12行代码的意义以及如何利用父节点查找:请看下面的图片
  分割操作
  虽然图片只有两种情况,别忘了有左右两个子节点,所有必须有四个if判断(代码中部分用三目式代替)。当l==arr[d].left&&r==arr[d].right(分割的目的范围与节点范围相同时返回值)
  6.单节点查询
  其实直接调用区间查询即可,两个范围一样就是单节点,这样效率不会影响,还是O(logn)//单点查询 直接调用区间查询即可 效率不受影响  int find(int d,int o)//d 为当前节点 o为查询节点 { 	return query(d,o,o);	 }
  7.区间修改
  8-12行代码原理如上图所示。将区间范围一步一步划分为节点操作,这样只需要一个递归就可以解决,具有很高的效率,别忘了父节点的值。void changeSegment(int d,int l,int r,int x)//l和r是修改的范围 x是范围内每一个节点改变的值 { 	if(arr[d].left==l&&arr[d].right==r&&arr[d].left==arr[d].right){//一定要修改叶子节点 不要误修改父节点 		arr[d].val+=x; 		return ; 	}else{ 		int mid = ((arr[d].left+arr[d].right)/2); 		if(l<=mid)		 			(r>mid)?changeSegment(d<<1,l,mid,x):changeSegment(d<<1,l,r,x); 		if(r>mid) 			(l<=mid)?changeSegment(d<<1|1,mid+1,r,x):changeSegment(d<<1|1,l,r,x); 		arr[d].val = arr[d<<1].val+arr[d<<1|1].val;//回溯 改变父节点的值 	} }测试总代码#include    struct Nd //定义线段树节点  { 	int left;//左孩子  	int right;//右孩子  	int val;//该节点值  };  Nd arr[1 << 10]; //随便设有1024个节点  //初始化  void build(int l,int r,int i)//利用递归构成线段树  { 	arr[i].left=l;//将左右区间的值赋值给节点  	arr[i].right=r; 	if(r==l) 	{ 		std::cin >> arr[i].val; 		return ; 	}  	build(l,(l+r)/2,i<<1);//递归到左节点  	build((r+l)/2+1,r,i<<1|1); //递归到右节点  	arr[i].val = arr[i<<1].val+arr[i<<1|1].val;//递归回溯时完成子节点对父节点的赋值  }  //打印节点  void dfs_print(int l,int r,int i) { 	/*if(r==l) 	{ 		std::cout << arr[i].val << " ";//打印叶子节点  		return ; 	}*/ 	std::cout << arr[i].val << " "; 	if(r==l) 	{ 		return ; 	} 	dfs_print(l,(l+r)/2,i<<1); 	dfs_print((r+l)/2+1,r,i<<1|1);  }  //更新节点  void update(int d,int r,int b)// d为当前节点 r为更新的节点 b为改变节点的值 这个递归算法实际就是二分查找  { 	if(arr[d].left==arr[d].right)//递归到叶子节点时 肯定下标是r 注意只能更改叶子节点  	{ 		arr[d].val=b;//更改值  		return; 	}	 	int mid = (arr[d].left+arr[d].right)>>1; 	if(r>mid)update(d<<1|1,r,b);//递归到右son 	else 	update(d<<1,r,b);//递归到左son 	arr[d].val = arr[d<<1].val+arr[d<<1|1].val;//递归回溯时完成子节点对父节点的赋值  }  //区间查询 我们不要忘记,父节点可是存储着它俩儿子值的和  int query(int d,int l,int r)//这个是返回区间的和  { 	int sum = 0; 	if(l==arr[d].left&&r==arr[d].right)//1.如果在目的范围内  		sum += arr[d].val; 	else{ 		int mid = ((arr[d].left+arr[d].right)/2); 		if(l<=mid)		 			(r>mid)?sum+=query(d<<1,l,mid):sum+=query(d<<1,l,r); 		if(r>mid) 			(l<=mid)?sum+=query(d<<1|1,mid+1,r):sum+=query(d<<1|1,l,r); 	} 	return sum; } //单点查询 直接调用区间查询即可 效率不受影响  int find(int d,int o)//d 为当前节点 o为查询节点 { 	return query(d,o,o);	 }   void changeSegment(int d,int l,int r,int x) { 	if(arr[d].left==l&&arr[d].right==r&&arr[d].left==arr[d].right){ 		arr[d].val+=x; 		return ; 	}else{ 		int mid = ((arr[d].left+arr[d].right)/2); 		if(l<=mid)		 			(r>mid)?changeSegment(d<<1,l,mid,x):changeSegment(d<<1,l,r,x); 		if(r>mid) 			(l<=mid)?changeSegment(d<<1|1,mid+1,r,x):changeSegment(d<<1|1,l,r,x); 		arr[d].val = arr[d<<1].val+arr[d<<1|1].val; 	} 	 } int main() { 	using namespace std; 	build(1,5,1); 	dfs_print(1,5,1); 	int n,a,x; 	cin >> n >> a >> x; 	/*update(1,n,a); 	dfs_print(1,5,1); 	cout << endl; 	cin >> n >> a; 	cout << "sum=" << query(1,n,a)<> n; 	cout << find(1,n);*/ 	changeSegment(1,n,a,x); 	dfs_print(1,5,1); 	return 0; } 感谢
  感谢观看,希望大家能够尽情的指点,如果有什么错误的地方可以指出来。
  创作不易,如果大家感觉有帮助请点一下赞,谢谢。
  下回会更新它的一道例题题解,链接如下所示。

跌下神坛的马云退出阿里股东创始人头衔被剥夺这是怎么了文一介布衣不做英语老师的马云带领着当年一伙人创建了阿里巴巴,摇身一变成为了多少创业青年的老师,在很长一段时间里,马云就是他们心目中的神,很多创业青年都梦想成为下一个马云。然而,口无高续航又好玩小鹏P5上市选哪款更合适?推荐2款给你如果说在预算有限的情况下,你更希望所买到的新能源车有哪些优点?我想大多数朋友想到的会是续航和智能化,说到这,最近刚刚上市的小鹏P5或许是这个问题最好的答案,所以今天我们来了解一下这携号转网难,携号跨城更难自从可以携号转网之后,大家都很方便了,换一个运营商就不需要再重新买一张卡了,毕竟现在大家的手机都捆绑着银行卡,支付宝微信等等的软件。不过以后如果发展成携号跨城那就最好了,因为很多人十年之前我不认识你,电动车销量至今已成长50倍过去10年电动车从几乎无人知晓,到如今成为各大产业竞逐的最热门目标,是什么因素推动电动车发展至今,而迈向全面电动化将带来哪些经济政治环境和社会冲击呢?2008年,特斯拉发布第一款能紫光展锐6nm5G芯片跑分超40万分中证网(记者吴科任)9月16日,国内芯片设计龙头紫光展锐举行UP2021展锐线上生态峰会,发布了多个5G创新成果。在5GtoC领域,紫光展锐曝光了6nm5G芯片的跑分成绩,超过40机器学习领域会成为求职主流吗?机器学习(ML)是人工智能(A。I。)的一个分支学科,是一项复杂的技能。然而,专家预测,对机器学习技能的需求在未来十年内会只增不减。实际上会这样吗?如果是这样,你将如何开始学习机器新能源车企太多了!工信部部长鼓励兼并重组9月13日,国务院新闻办公室(简称国新办)举行推进制造强国网络强国建设助力全面建成小康社会发布会,在发布会上,工信部部长肖亚庆表示,现在新能源汽车企业数量太大,处于小而散的状况,鼓德创企rooom获600万欧元融资ambr将推出元宇宙平台xambr(VRPinea9月17日讯)今日重点新闻德国创企rooom宣布获600万欧元融资,本轮融资将用于构建rooom3D虚拟体验平台江西科骏实业有限公司完成1。2亿元人民币融资,本轮融OPPO想三分天下,有这个实力吗?昨晚,OPPO举行了秋季新品发布会,带来了OPPOFindX3Pro摄影师版OPPOWatch2心电图版,以及带来更多万物互联想象的ColorOS12。产品本身属于预期之中,但OP没有了美团,是不是大部分餐馆都要关门?美团存在的价值大吗?以前没有美团的时候餐馆都关门了吗?美团存在是有价值的,但是要看对什么餐馆。比如对于只做外卖的餐馆来说美团的存在是至关重要的,因为他们是要靠美团来销售的。但是对于可以堂食的来说价值就iOS14。8最详细体验,不同机型对比,没升级的小伙伴进来看看当大家都沉浸在iPhone13带来的新鲜感的时候,对于一些老用户来说,想买13却又舍不得六七千块钱,那么怎么办呢?当然是升级系统,用旧手机体验新手机的感觉。苹果也是非常的仗义,在i
终结行业暴利!小米笔记本Pro花最少的钱,享受最顶级的配置一款好的工具会让你在工作的时候更加得心应手,工作效率翻倍。大家应该或多或少都对OLED的屏幕有所了解,无奈的屏幕在全黑的时候更加省电,而且显示色彩会更加的鲜艳,比LED屏幕更加的轻捐赠清华大学977680元!这次雷军又火了一把小米最近可以说是又火了一把,对清华大学的捐赠直接刷爆了朋友圈引发网友们怒赞。而我认为小米这一次的捐赠真的值得让我们点赞,首先与清华大学的捐赠让我们看到了小米校友们的感恩。在母校11格力看了也直呼内行!小米新风空调24小时只要0。3度电?小米这场新品发布会发布的这款新风空调,真的给了我很大的惊喜,首先第1点就是智能化作为小米智能生态链中的智能空调,这一次米家新风库的尊享版可以通过米家实现全屋互联,可以和更多的小米智小米生生不息百城同跑一万个米粉一起干了件大事这次米粉节厉害了!除了举行生生不息百城同跑活动,有超过一万名米粉参与之外,还取得了全渠道总支付金额46。1亿的成绩,这是比去年翻了一倍的成绩,表现十分惊艳了。而且第5000家小米之奥睿科户外储能电源五一自驾旅游必备用电伴侣如果有人问我什么时候开始有了想给自己置办一个户外电源的想法?起因就是我前段时间无聊在网上刷视频,刷到很多的自驾旅行或者改造房车的一些视频博主的视频,他们出行途中需要解决的最大问题就飞行模式除了飞机上使用,还有什么用处?手机有一个功能叫飞行模式,飞行模式其实最初目的是为了让用户在乘坐飞机途中,让手机不发射不接收信号,不干扰飞机上的仪器。但飞行模式其实还有这几个实用的地方用处防广告骚扰和诈骗骚扰我们手机屏幕脏了千万别用衣服擦!教你一招让屏幕恢复如新,太厉害了手机屏幕脏了你会怎么办?多数人随手拿到什么就擦,比如衣服纸巾。有些人就不大一样你们千万别跟着跟着学你手机上已经有100万个细菌,并且每天都在接触你的皮肤。手机细菌会给我们带来哪些隐手机越用越卡?打开这些功能,再用几年也不卡很多朋友抱怨手机越用越慢,经常卡,有什么办法能解决呢?今天小宁君就教大家6个方法,让你的手机可以恢复以往的流畅PART1清理APP缓存清理缓存,打开vivo手机i管家即可轻松清理,手机剩1的电,能用多久?你知道吗?有的手机最后1的电量最耐用,每次都够看完一个十分钟的视频。而有的手机,每次电池报警还有10的电量,你以为还能再撑一会儿,去找个充电器的时间手机就关机了其实,手机电池的剩余电量很难估秋天的第一杯奶茶刷屏了!什么梗?昨天,朋友圈被这样一句话刷屏了秋天的第一杯奶茶跟不上网速的还不止小编一个什么奶茶?要52块钱???昨天下午秋天的第一杯奶茶甚至冲上了微博热搜!所以,这到底是什么意思呢?有人说是因为你在放假,骗子却在加班,警惕这几种诈骗十一中秋双节庆吃吃喝喝玩玩闹闹的时间到伙伴们是不是分外逍遥呢?但是!骗子们可没有放假说不定正在暗处想方设法把你的钱骗走这份防骗攻略,请收好!骗术一ETC卡异常诈骗十一中秋双节假期,