树形结构1
堆
定义:堆(Heap)是计算机科学中一类特殊的数据结构,是最高效的优先级队列。堆通常是一个可以被看做一棵完全二叉树的数组对象。
堆分为大根堆和小根堆(从小到大用大根堆,从大到小用小根堆)
堆排序:
1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。
#include <bits/stdc++.h> #define N 1000 using namespace std; void max_heapify(int arr[], int left, int right) { int dad = left; int son = dad * 2 + 1; while( son <= right ) { if( son + 1 <= right && arr[son] < arr[son + 1]) son++; if( arr[dad] > arr[son] ) return ; else { swap(arr[dad],arr[son]); dad = son; son = dad * 2 + 1; } } } void heap_sort(int arr[],int len) { int i; for( i = len / 2 - 1; i >= 0; i-- ) max_heapify(arr, i, len - 1); for( i = len - 1; i > 0; i--) { swap(arr[0], arr[i]); max_heapify(arr, 0, i - 1); } } int main() { int n; int arr[N]; cin>>n; for(int i = 0; i < n; i++) cin>>arr[i]; heap_sort(arr,n); for(int i = 0; i < n; i++) cout<<arr[i]<<' '; return 0; }
并查集
定义:并查集是一种特殊的数据结构(它的逻辑结构本质也是一颗“树”,有唯一的根节点,任意数的子节点),它的特殊在于它只定义了两种数据操作(查找和合并)。这是用来解决连通性问题,查找(find):就是查找任意两个节点是否连通,就是是否有共同的祖先(节点找它的父节点的过程,一层一层地找)。合并(union):把两个不同集合的节点合并在一起。
例题:食物链(P2024)
#include<bits/stdc++.h> #define maxn 150005 using namespace std; int fa[maxn]; int ans; int findFa(int x) { //只有x的祖父节点的根节点才为本身 if(x==fa[x])return x; //递归找跟节点,这里对并查集高度进行优化,变为2层不懂可以自己画个图! return fa[x]=findFa(fa[x]); } inline void Union(int x,int y) { //先找到x,y的根节点,然后fa[x的根节点]指向y的根节点,即可合并成功! fa[findFa(x)]=findFa(y); } //判断是否在一个集合中 inline bool sameSet(int x,int y) { return findFa(x)==findFa(y); } int main() { int n,k; scanf("%d%d",&n,&k); for(int i=1; i<=3*n; i++) { fa[i]=i; } for(int i=1; i<=k; i++) { int c,x,y; scanf("%d%d%d",&c,&x,&y); if(x<=n&&y<=n) { //c==1,x和y是同类 if(c==1) { //x和y的猎物,x和y的天敌都不是同一类,就Union他们,否则ans++ if(!sameSet(x,y+n)&&!sameSet(x,y+2*n)) { Union(x,y); Union(x+n,y+n); Union(x+2*n,y+2*n); } else { ans++; } } else //c==2,x吃y { if(x!=y) { //x和y不是同一类,x的天敌y+n和x也不是同一类 if(!sameSet(x,y)&&!sameSet(x,y+n)) { //得到新关系 Union(x+n,y); Union(x+2*n,y+n); Union(x,y+2*n); } else { //x和y是同一类 ans++; } } else {//x和y同类,与c不符合,ans++ ans++; } } } else//当输入的x或y大于N时越界,ans++ { ans++; } } printf("%d",ans); return 0; }
线段树
定义:线段树是一种二叉搜索树(实质是平衡二叉树),线段树的每个结点都存储了一个区间,也可以理解成一个线段。
作用:线段树的适用范围很广,可以维护修改以及查询区间上的最值、求和。更可以扩充到二维线段 树(矩阵树)和三维线段树(空间树)。对于一维线段树来说,每次更新以及查询的时间复杂 度为O(log n)。
事实上,线段树多用于解决区间问题,但并不是线段树只能解决区间问题。
线段树的代码长到离谱,背起来会有亿点点困难
例题
P2023 [AHOI2009]维护序列
#include<bits/stdc++.h> using namespace std; #define maxn 100005 #define ll long long #define inf 1000000009000000000 #define IOS ios::sync_with_stdio(false) ll mod; struct e { ll left, right; //该节点的区间范围 ll add; //区间增量 ll mul; //区间乘量 ll sum; //区间和,包括add在内 }; e tree[4*maxn]; ll a[maxn];//记录原始值 //建树,从上往下建 ll build(ll s, ll t, ll num) { ll i, j; tree[num].left = s; tree[num].right = t; tree[num].add = 0; tree[num].mul=1; if (s != t) { i = build(s, (s + t) / 2, num * 2); j = build((s + t) / 2 + 1, t, num * 2 + 1); } else//s等于t说明是最底层 { tree[num].sum = a[s]; return(a[s]); } tree[num].sum = i + j;//求和 tree[num].sum%=mod; return(tree[num].sum); } void insert(ll s, ll t, ll add,ll mul,ll num) { //如果发现是s,t的子集,标记并修改后返回 if (s <= tree[num].left&&tree[num].right <= t) { tree[num].add*=mul; tree[num].add += add; tree[num].sum*=mul; tree[num].sum += add * (tree[num].right - tree[num].left + 1); tree[num].sum%=mod; tree[num].mul*=mul; tree[num].add%=mod; tree[num].mul%=mod; //cout<<"add="<<tree[num].add<<endl; return; } //发现不是s,t的子集,检查是否被标记过,分散到左右子节点中,重置add //为什么要执行这个操作,因为后面需要标记它后面的某个子集,而后面的子集原来都是没标记的,所以必须提前分配 if (tree[num].add||tree[num].mul!=1) { tree[num*2].sum*=tree[num].mul; tree[num * 2].sum += tree[num].add*(tree[num * 2].right - tree[num * 2].left + 1); tree[num*2].sum%=mod; tree[num*2+1].sum*=tree[num].mul; tree[num * 2 + 1].sum += tree[num].add*(tree[num * 2 + 1].right - tree[num * 2 + 1].left + 1); tree[num*2+1].sum%=mod; tree[num*2].mul*=tree[num].mul; tree[num*2+1].mul*=tree[num].mul; tree[num*2].add*=tree[num].mul; tree[num * 2].add += tree[num].add; tree[num*2+1].add*=tree[num].mul; tree[num * 2 + 1].add += tree[num].add; tree[num*2].mul%=mod; tree[num*2+1].mul%=mod; tree[num * 2].add %=mod; tree[num * 2 + 1].add %=mod; tree[num].add = 0; tree[num].mul=1; } if (s <= (tree[num].left + tree[num].right) / 2) insert(s, t, add, mul,num * 2); if (t>(tree[num].left + tree[num].right) / 2) insert(s, t, add, mul,num * 2 + 1); tree[num].sum = tree[num * 2].sum + tree[num * 2 + 1].sum; tree[num].sum%=mod; } ll search(ll s, ll t, ll num) { ll i = 0, j = 0; //搜索到是其子集,返回sum if (s <= tree[num].left&&tree[num].right <= t) { //cout<<tree[num].left<<" "<<tree[num].right<<" "<<tree[num].sum<<endl; return(tree[num].sum); } //与修改一样,必须分配下去 if (tree[num].add||tree[num].mul!=1) { tree[num*2].sum*=tree[num].mul; tree[num * 2].sum += tree[num].add*(tree[num * 2].right - tree[num * 2].left + 1); tree[num*2].sum%=mod; tree[num*2+1].sum*=tree[num].mul; tree[num * 2 + 1].sum += tree[num].add*(tree[num * 2 + 1].right - tree[num * 2 + 1].left + 1); tree[num*2+1].sum%=mod; tree[num*2].mul*=tree[num].mul; tree[num*2+1].mul*=tree[num].mul; tree[num*2].add*=tree[num].mul; tree[num * 2].add += tree[num].add; tree[num*2+1].add*=tree[num].mul; tree[num * 2 + 1].add += tree[num].add; tree[num*2].mul%=mod; tree[num*2+1].mul%=mod; tree[num * 2].add %=mod; tree[num * 2 + 1].add %=mod; tree[num].add = 0; tree[num].mul=1; } if (s <= (tree[num].left + tree[num].right) / 2) { i = search(s, t, num * 2); i%=mod; } if (t>(tree[num].left + tree[num].right) / 2) { j = search(s, t, num * 2 + 1); j%=mod; } return(i + j)%mod; } int main() { IOS; ll k, n, m, p, t; cin>>n>>mod; for (ll i = 1; i <= n; i++) cin>>a[i]; build(1, n, 1); cin>>m; for (ll i = 1; i <= m; i++) { ll s,a,b,c; cin>>s; if(s==1) { cin>>a>>b>>c; c%=mod; insert(a,b,0,c,1); } else if (s == 2) { cin>>a>>b>>c; c%=mod; insert(a, b, c, 1, 1); } else { cin>>a>>b; cout << search(a, b, 1) << endl; } } return(0); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】