日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

数据结构 —— 线段树

發(fā)布時(shí)間:2025/3/17 编程问答 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构 —— 线段树 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

【概述】

線段樹是一種二叉搜索樹,其存儲(chǔ)的是一個(gè)區(qū)間的信息,每個(gè)結(jié)點(diǎn)以結(jié)構(gòu)體的形式去存儲(chǔ),每個(gè)結(jié)構(gòu)體包含三個(gè)元素:區(qū)間左端點(diǎn)、區(qū)間有端點(diǎn)、該區(qū)間要維護(hù)的信息(視實(shí)際情況而定),其基本思想是分治的思想。

其特點(diǎn)是:

  • 每個(gè)節(jié)點(diǎn)的左孩子區(qū)間范圍為 [l,mid],右孩子為 [mid+1,r]
  • 對(duì)于結(jié)點(diǎn) k,左孩子結(jié)點(diǎn)為 2*k,右孩子為 2*k+1,符合完全二叉樹的性質(zhì)

線段樹一般結(jié)構(gòu)如圖:

【基礎(chǔ)操作實(shí)現(xiàn)】

線段樹的基礎(chǔ)操作主要有 5 個(gè):建樹、單點(diǎn)查詢、單點(diǎn)修改、區(qū)間查詢、區(qū)間修改

結(jié)點(diǎn):

struct node{int l,r;//區(qū)間左右端點(diǎn)int w;//區(qū)間和 }tree[4*n+1];//樹開4倍空間。

?以下的實(shí)現(xiàn)均以求區(qū)間和為例

1.建樹

1)思路

  • 對(duì)于二分到的每一個(gè)結(jié)點(diǎn),給它的左右端點(diǎn)確定范圍
  • 如果是葉子節(jié)點(diǎn),存儲(chǔ)要維護(hù)的信息
  • 狀態(tài)合并

2)實(shí)現(xiàn)

void build(int l,int r,int k){tree[k].l=l;tree[k].r=r;if(l==r){//葉子節(jié)點(diǎn) scanf("%d",&tree[k].w);return; }int mid=(l+r)/2;buildTree(l,mid,k*2);//左孩子 buildTree(mid+1,r,k*2+1);//右孩子tree[k].w=tree[k*2].w+tree[k*2+1].w;//狀態(tài)合并,此結(jié)點(diǎn)的w=兩個(gè)孩子的w和 }

2.單點(diǎn)查詢

1)思路

單點(diǎn)查詢即查詢一個(gè)點(diǎn)的狀態(tài),其查詢方法與二分查詢法基本一致。

若當(dāng)前枚舉的點(diǎn)左右端點(diǎn)相等,即為葉節(jié)點(diǎn)時(shí),就是最終的目標(biāo)節(jié)點(diǎn)。

若當(dāng)前枚舉的點(diǎn)左右端點(diǎn)不等,設(shè)查詢位置為 x,當(dāng)前結(jié)點(diǎn)區(qū)間范圍為?l、r,中點(diǎn)為 mid,則若?x<=mid,則遞歸它的左孩子,否則遞歸它的右孩子。

2)實(shí)現(xiàn)

void queryNode(int k){if(tree[k].l==tree[k].r){//當(dāng)前結(jié)點(diǎn)的左右端點(diǎn)相等,為葉子節(jié)點(diǎn),是最終答案 ans=tree[k].w;return;}int mid=(tree[k].l+tree[k].r)/2;if(x<=mid)//目標(biāo)位置比中點(diǎn)靠左,就遞歸左孩子 queryNode(k*2);else//反之,遞歸右孩子 queryNode(k*2+1); }

3.單點(diǎn)修改

1)思路

單點(diǎn)修改即更改某一個(gè)點(diǎn)的狀態(tài),對(duì)第 x 個(gè)數(shù)加上 y,其基本思想是結(jié)合單點(diǎn)查詢的原理,找到 x 的位置,然后根據(jù)建樹狀態(tài)合并的原理,修改每個(gè)結(jié)點(diǎn)的狀態(tài)。

2)實(shí)現(xiàn)

void updateNode(int k){if(tree[k].l==tree[k].r){//找到目標(biāo)位置 tree[k].w+=y;return;}int mid=(tree[k].l+tree[k].r)/2;if(x<=mid)//目標(biāo)位置比中點(diǎn)靠左,就遞歸左孩子updateNode(k*2);else//反之,遞歸右孩子updateNode(k*2+1);tree[k].w=tree[k*2].w+tree[k*2+1].w;//所有包含結(jié)點(diǎn)k的結(jié)點(diǎn)狀態(tài)更新 }

4.區(qū)間查詢

1)思路

區(qū)間查詢,即查詢一段區(qū)間的狀態(tài)

2)實(shí)現(xiàn)

void queryInterval(int k,int x,int y){if(tree[k].l>=x&&tree[k].r<=y){ans+=tree[k].w;return;}int mid=(tree[k].l+tree[k].r)/2;if(x<=mid) queryInterval(k*2,x,y);if(y>mid) queryInterval(k*2+1,x,y); }

5.區(qū)間修改

1)思路

區(qū)間修改即修改一段連續(xù)區(qū)間的值,給區(qū)間 [a,b] 的每個(gè)數(shù)都加 x

線段樹更新樹時(shí),為了避免更新而導(dǎo)致超時(shí)問題,因此每次修改只修改相對(duì)應(yīng)的區(qū)間,然后記錄一個(gè)延遲標(biāo)記,其作用是:存儲(chǔ)到這個(gè)節(jié)點(diǎn)的修改信息,暫時(shí)不把修改信息傳到子節(jié)點(diǎn)。簡(jiǎn)單來說,每次更新的時(shí)候不要更新到底,用延遲標(biāo)記使得更新延遲到下次需要更新 or 詢問的時(shí)候。

下次更新或者查詢的時(shí)候,如果查到該節(jié)點(diǎn),就把延遲標(biāo)記進(jìn)行下傳,將值加到他的子節(jié)點(diǎn)上去,同時(shí)將延遲標(biāo)記變?yōu)?0,避免下次重復(fù)更新。這樣只更新到查詢的子區(qū)間,不需要再往下找了,極大的降低了時(shí)間復(fù)雜度。

以下圖為例,一開始對(duì)區(qū)間 [1,4] 每個(gè)值都 +3,只有當(dāng)需要對(duì) [3,4] 區(qū)間查詢時(shí),才對(duì)下面的區(qū)間進(jìn)行更新,其他區(qū)間無(wú)需更新。

具體操作:

  • 原結(jié)構(gòu)體中增加新的變量,存儲(chǔ)這個(gè)標(biāo)記
  • 遞歸到這個(gè)節(jié)點(diǎn)時(shí),只更新這個(gè)節(jié)點(diǎn)的狀態(tài),并把當(dāng)前的更改值累積到標(biāo)記中
  • 當(dāng)需要遞歸這個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn)時(shí),標(biāo)記下傳給子節(jié)點(diǎn),此時(shí)不必是哪個(gè)子節(jié)點(diǎn),兩個(gè)都傳下去

下傳操作的原理:

  • 當(dāng)前節(jié)點(diǎn)的標(biāo)記累積到子節(jié)點(diǎn)的標(biāo)記中
  • 修改子節(jié)點(diǎn)狀態(tài),在當(dāng)前的求和實(shí)例中,即原狀態(tài)+子節(jié)點(diǎn)區(qū)間點(diǎn)的個(gè)數(shù)*父節(jié)點(diǎn)傳下來的標(biāo)記
  • 父節(jié)點(diǎn)標(biāo)記清 0

2)實(shí)現(xiàn)

標(biāo)記下傳:

void pushDown(int k){tree[k*2].f+=tree[k].f;//左孩子更新延遲標(biāo)記tree[k*2+1].f+=tree[k].f;//右孩子更新延遲標(biāo)記tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);//左孩子狀態(tài)更新tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);//右孩子狀態(tài)更新tree[k].f=0;//當(dāng)前延遲標(biāo)記清零 }

區(qū)間修改:

void updateInterval(int k,int x,int y){if(tree[k].l>=x&&tree[k].r<=y){//當(dāng)前區(qū)間全部對(duì)要修改的區(qū)間有用tree[k].w+=(tree[k].r-tree[k].l+1)*x;//(r-1)+1區(qū)間點(diǎn)的總數(shù)tree[k].f+=x;return;}if(tree[k].f)//標(biāo)記下傳。只有不滿足上面的if條件才執(zhí)行,所以一定會(huì)用到當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn) pushDown(k);int mid=(tree[k].l+tree[k].r)/2;if(x<=mid) updateInterval(k*2,x,y);if(y>mid) updateInterval(k*2+1,x,y);tree[k].w=tree[k*2].w+tree[k*2+1].w;//更改區(qū)間狀態(tài) }

【模版】

1.單點(diǎn)更新+區(qū)間查詢

以求和為例,具體情況根據(jù)題意

struct Node{int l,r;//左右區(qū)間int sum;//區(qū)間和 } tree[N*4]; int a[N]; void pushUp(int i){//維護(hù)子結(jié)點(diǎn)tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; } void build(int i,int l,int r){ //建樹tree[i].l=l;tree[i].r=r;if(l==r){//葉節(jié)點(diǎn)tree[i].sum=a[l];//邊輸入邊建樹//scanf("%d",&a[i]);return;}int mid=(l+r)>>1;build(i*2,l,mid);//結(jié)點(diǎn)的左兒子build(i*2+1,mid+1,r);//結(jié)點(diǎn)的右兒子pushUp(i); }//對(duì)id號(hào)點(diǎn)進(jìn)行修改 void update(int i,int id,int val){//線段樹單點(diǎn)修改if(tree[i].l==tree[i].r){tree[i].sum+=val;return;}int mid=(tree[i].l+tree[i].r)/2;if(id<=mid)update(i*2,id,val);if(id>mid)update(i*2+1,id,val);pushUp(i); }int query(int i,int ql,int qr){//線段樹區(qū)間查詢if(ql<=tree[i].l&&qr>=tree[i].r)//當(dāng)前區(qū)間在目標(biāo)區(qū)間內(nèi)return tree[i].sum;int mid=(tree[i].l+tree[i].r)/2;int res=0;if(ql<=mid)res+=query(i*2,ql,qr);if(qr>mid)res+=query(i*2+1,ql,qr);return res; }int main(){int n,m;cin>>n;for(int i=1;i<=n;i++)//初始值cin>>a[i];build(1,1,n);//先輸入再建樹cin>>m;//m組詢問while(m--){int p;cin>>p;if(p==1){//單點(diǎn)更新int id,val;cin>>id>>val;update(1,id,val);}else if(p==2){//區(qū)間查詢int a,b;cin>>a>>b;cout<<query(1,a,b)<<endl;}}return 0; }

2.區(qū)間更新+區(qū)間查詢

struct Node{int l,r;//左右區(qū)間int sum;//區(qū)間和int maxx,minn;//區(qū)間最值int lazyAdd;//區(qū)間增值時(shí)的延遲標(biāo)記int lazySet;//區(qū)間賦值時(shí)的延遲標(biāo)記 }tree[N*4]; int a[N]; int resSum,resMax,resMin;//存儲(chǔ)結(jié)果 void pushDown(int i){//標(biāo)記下傳if(tree[i].lazySet!=-1){tree[i*2].lazySet=tree[i*2+1].lazySet=tree[i].lazySet;tree[i*2].lazyAdd=tree[i*2+1].lazyAdd=0;tree[i*2].minn=tree[i*2+1].minn=tree[i].lazySet;tree[i*2].maxx=tree[i*2+1].maxx=tree[i].lazySet;tree[i*2].sum=(tree[i*2].r-tree[i*2].l+1)*tree[i].lazySet;tree[i*2+1].sum=(tree[i*2+1].r-tree[i*2+1].l+1)*tree[i].lazySet;tree[i].lazySet=-1;}///左子節(jié)點(diǎn)tree[i*2].lazyAdd+=tree[i].lazyAdd;//打上延遲標(biāo)記tree[i*2].minn+=tree[i].lazyAdd;//更新tree[i*2].maxx+=tree[i].lazyAdd;//更新tree[i*2].sum+=tree[i].lazyAdd*(tree[i*2].r-tree[i*2].l+1);//更新///右子節(jié)點(diǎn)tree[i*2+1].lazyAdd+=tree[i].lazyAdd;//打上延遲標(biāo)記tree[i*2+1].minn+=tree[i].lazyAdd;//更新tree[i*2+1].maxx+=tree[i].lazyAdd;//更新tree[i*2+1].sum+=tree[i].lazyAdd*(tree[i*2+1].r-tree[i*2+1].l+1); //更新tree[i].lazyAdd=0;//清除標(biāo)記 }void pushUp(int i){//維護(hù)子節(jié)點(diǎn)tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;tree[i].maxx=max(tree[i*2].maxx,tree[i*2+1].maxx);tree[i].minn=min(tree[i*2].minn,tree[i*2+1].minn); }void build(int i,int l,int r){//建樹tree[i].l=l;tree[i].r=r;tree[i].lazyAdd=0;tree[i].lazySet=-1;if(l==r){//葉結(jié)點(diǎn)tree[i].sum=a[l];tree[i].maxx=a[l];tree[i].minn=a[l];return;}int mid=(l+r)>>1;build(i*2,l,mid);//結(jié)點(diǎn)左兒子build(i*2+1,mid+1,r);//結(jié)點(diǎn)右兒子pushUp(i); }void updateSet(int i,int ql,int qr,int val){//區(qū)間修改,整體賦值為valif(tree[i].l>=ql && tree[i].r<=qr){tree[i].sum=val*(tree[i].r-tree[i].l+1);tree[i].minn=val;tree[i].maxx=val;tree[i].lazySet=val;tree[i].lazyAdd=0;return;}pushDown(i);//標(biāo)記下傳int mid=(tree[i].l+tree[i].r)/2;if(ql<=mid)updateSet(i*2,ql,qr,val);if(qr>mid)updateSet(i*2+1,ql,qr,val);pushUp(i); }void updateAdd(int i,int ql,int qr,int val){//區(qū)間修改,整體+valif(tree[i].l>=ql&&tree[i].r<=qr){tree[i].sum+=val*(tree[i].r-tree[i].l+1);tree[i].minn+=val;tree[i].maxx+=val;tree[i].lazyAdd += val;return;}pushDown(i);//標(biāo)記下傳int mid=(tree[i].l+tree[i].r)/2;if(ql<=mid)updateAdd(i*2,ql,qr,val);if(qr>mid)updateAdd(i*2+1,ql,qr,val);pushUp(i); }void query(int i,int ql,int qr){//區(qū)間查詢if(ql<=tree[i].l && tree[i].r<=qr){resSum+=tree[i].sum;resMax=max(resMax,tree[i].maxx);resMin=min(resMin,tree[i].minn);return ;}pushDown(i);int mid=(tree[i].l+tree[i].r)/2;if(ql<=mid)query(i*2,ql,qr);if(qr>mid)query(i*2+1,ql,qr);pushUp(i); }int main(){int n;cin>>n;for(int i=1;i<=n;i++)cin>>a[i];build(1,1,n);int m;cin>>m;while(m--){int p;cin>>p;if(p==1){//區(qū)間整體賦值int a,b;//區(qū)間int val;//值scanf("%d%d%d",&a,&b,&val);updateSet(1,a,b,val);}else if(p==2){//區(qū)間整體加值int a,b;//區(qū)間int val;//值scanf("%d%d%d",&a,&b,&val);updateAdd(1,a,b,val);}else if(p==3){//區(qū)間查詢int a,b;cin>>a>>b;resSum=0,resMax=-INF,resMin=INF;query(1,a,b);cout<<"Sum="<<resSum<<endl;cout<<"Max="<<resMax<<endl;cout<<"Min="<<resMin<<endl;}}return 0; }

【例題】

  • I Hate It(HDU-1754)(求最值+單點(diǎn)更新):點(diǎn)擊這里
  • A Simple Problemwith Integers(POJ-3468)(區(qū)間和+區(qū)間更新):點(diǎn)擊這里
  • Naive Operations(HDU-6315)(維護(hù)技巧+區(qū)間更新):點(diǎn)擊這里
  • Can you answer these queries?(HDU-4027)(開方操作+區(qū)間更新):點(diǎn)擊這里
  • Tunnel Warfare(HDU-1540)(區(qū)間合并):點(diǎn)擊這里
  • 肥豬(2019牛客寒假算法基礎(chǔ)集訓(xùn)營(yíng) Day6-H)(區(qū)間最小值):點(diǎn)擊這里

總結(jié)

以上是生活随笔為你收集整理的数据结构 —— 线段树的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美aa一级| www激情 | 国产小视频网址 | 找个毛片看看 | 狠狠丁香| 国产精品扒开腿做爽爽爽a片唱戏 | 国产精品综合在线 | 中文字幕在线视频网 | 亚洲av毛片成人精品 | 欧美日韩黑人 | 在线尤物 | 国产精品jizz在线观看美国 | 丝袜美女av| www.一起操| 欧美13p| 久久99操 | 69视频国产 | 久久久久久久久久久影院 | 免费做a爰片77777 | 日韩欧美一区二区三区在线 | 污污内射久久一区二区欧美日韩 | 国产精品porn | 成人av免费| 大奶子情人 | 69**夜色精品国产69乱 | 91激情视频在线 | 亚洲v国产v欧美v久久久久久 | 天天躁日日躁狠狠躁av麻豆 | 奇米精品一区二区三区在线观看一 | 麻豆一区产品精品蜜桃的特点 | 天天干天天插 | 色婷婷91| 久久99中文字幕 | 久久亚洲精精品中文字幕早川悠里 | 色女人在线 | 人人爱人人 | 亚洲日本欧美精品 | 日本高清视频在线观看 | 欧美久操| 亚洲av无码专区国产乱码不卡 | 灌满闺乖女h高h调教尿h | 国产18一19sex性护士 | 色亚洲欧美 | 精品成人免费视频 | 国产做爰xxxⅹ性视频国 | 吊侵犯の奶水授乳羞羞漫画 | 在线视频日韩欧美 | 久久三级精品 | 中文字幕丰满孑伦无码专区 | 欧美日韩一区二区三区不卡视频 | 麻豆 美女 丝袜 人妻 中文 | 91成人破解版 | 日本特黄特色aaa大片免费 | 日韩精品系列 | av成人在线看| 尤物视频在线播放 | 久久发布国产伦子伦精品 | 国产精品卡一卡二 | 强行挺进皇后紧窄湿润小说 | 亚洲免费看片 | 丁香婷婷激情 | 色播久久 | 伊人狼人综合 | 亚洲av无码一区二区三区网址 | 污网站在线看 | 天堂综合在线 | av福利在线播放 | 黄色片网站在线 | 四虎成人精品在永久免费 | 欧美无玛 | 欧美老熟妇一区二区 | 总裁边开会边做小娇妻h | 日韩大片免费看 | 永久免费在线观看视频 | 色妞www精品视频 | 99热99| 午夜神马福利 | 国产精品3区 | 人人射影院 | 黄色正能量网站 | 黄色三级在线观看 | 国产福利在线播放 | 成人乱码一区二区三区av | 欧美精品在欧美一区二区 | ⅹxxxxhd亚洲日本hd老师 | 欧美日韩一区二区三区 | 成人无码一区二区三区 | 老湿机69福利区午夜x片 | 欧美成人猛片aaaaaaa | www.av88| 日韩成人在线免费观看 | 超碰在线98 | 国产午夜大地久久 | 致命魔术电影高清在线观看 | 亚洲永久网站 | 欧美操女人 | 韩日黄色片 | 日韩在线网 | 琪琪色视频 |