标签:return int MAX else rch 节点 2.4
2.4 一些数据结构
1.树和二叉树
2.优先队列和堆
优先队列:
可以插入一个数值
取出最小的数值(获得数值,并且删除)
注意:能使用让二叉树来实现一个优先队列的数据结构,是 ”堆“
堆:
堆操作时间复杂度 O(n):因为堆两种操作所花的时间都和树的深度成正比
//注意 是小根堆 // 数组实现堆(数组从0索引开始存储数据) //首先 对任一节点i 其父节点为a[(i-1)/2] // 左孩子节点为2*i+1 右孩子节点为 2*i+2 int heap[MAX], sz = 0; //显然 sz是堆中数据总长度 其指向第一个未存储数据的地方 void push(int x) { //获得x将插入的初始位置 即二叉树尾部第一个未存储数据的地方 int i = sz++; // 开始比较父节点 直至合适位置 while (i > 0) { int p = (i - 1) / 2; if (heap[p] <= x) { break; } heap[i] = heap[p]; i = p; } //找到位置 填值 heap[i] = x; } //返回最小值 并将其删除 之后重构堆 int pop() { int res = heap[0]; //记录返回值 //再将其删除 即将最后的值提到根的位置 heap[0] = heap[sz - 1]; sz--; //再次重构堆 //从根开始向下交换 int i = 0; //查看左孩子是否存在 while (i*2+1 < sz) { int minSon = i * 2 + 1; //若右孩子存在 //获得最小的孩子节点 if (2 * i + 2 < sz) { int minSon = heap[2 * i + 1] <= heap[2 * i + 2] ? 2 * i + 1 : 2 * i + 2; } if (heap[i] <= heap[minSon]) { break; } heap[i] = heap[minSon]; i = minSon; } return res; }
Expedition
首先,我们认为在到达某个加油站后,获得了加该站 汽油的权利
当在未到达下一站之际,如果没有油,只需认为在该站加了汽油
而每次加油时,很明显应该 认为在加油量最大的站点加了油
由此:该问题有了很好的解决方法
每当我们驶过一个站 就将该站油量加入优先队列
当油量不足时,取出优先队列最大值 并认为在那个站点加了油
那么如果此时队列为空,则无法到达,否则加油次数加一
#include<bits/stdc++.h> using namespace std; const int N_MAX=2e4+5; int N,L,P; vector<pair<int ,int>> vec(N_MAX); void solve() { //首先 按离起点排序 for(int i=0;i<N;i++) vec[i].first=L-vec[i].first; sort(vec.begin(),vec.begin()+N);//这里需要注意一下 范围 int count=0,pos=0;//计数,当前位置,并且认为P为当前油量 //将终点也添加到加油站 方便计算 //当油量不足以到达终点输出-1 否则输出count即可 vec[N].first=L; vec[N].second=0; N++; //准备一个优先队列 大根堆 priority_queue<int,vector<int>,less<int>> que; for(int i=0;i<N;i++) { int d=vec[i].first-pos; while(P<d) { if(que.empty()) { cout<<-1; return; } //油量不足便从优先队列中拿去油量最多的油 P+=que.top(); que.pop(); count++; } //到达一个加油站 便获取了在该站加油的机会 加入队列中 //更新油量 和位置 que.push(vec[i].second); P-=d; pos=vec[i].first; } cout<<count; } int main() { cin>>N; for(int i=0;i<N;i++)cin>>vec[i].first>>vec[i].second; cin>>L>>P; solve(); return 0; }
Fence_Repair
#include<bits/stdc++.h> using namespace std; int n; const int MAX=2e4+5; int arr[MAX]; void solve() { long long ans=0,temp=0; // int L=n; while(n>1) { int min1=0,min2=1; if(arr[min1]>arr[min2])swap(min1,min2); //遍历数组 使最小min1 最大min2 for(int i=2;i<n;i++) { if(arr[i]<arr[min1]) { min2=min1; min1=i; }else if(arr[i]<arr[min2]) { min2=i; } } //拼接节点 temp=arr[min1]+arr[min2]; ans+=temp; //把temp填入数组 并把arr[n-1]的位置用于取消掉 //用min1来填入temp min2把原来的arr[n-1]位置的数记录 if(min1==n-1)swap(min1,min2); arr[min1]=temp; arr[min2]=arr[n-1]; n--; // L--; } cout<<ans; } //引入优先队列 void solve1() { long long ans=0; // priority_queue<int> que;//默认从大到小 // priority_queue<int,vector<int>,less<int> >q;//此为从大到小 // priority_queue<int,vector<int>,greater<int> >q;//此为从小到大 priority_queue<int,vector<int>,greater<int> >que; for(int i=0;i<n;i++) { que.push(arr[i]); } while(que.size()>1) { int l1,l2; l1=que.top(); que.pop(); l2=que.top(); que.pop(); que.push(l1+l2); ans+=l1+l2; } cout<<ans; } int main() { cin>>n; for(int i=0;i<n;i++)cin>>arr[i]; solve1(); return 0; }
3.二叉搜索树
O(logN)
二叉搜索树是能够高效的进行如下操作的数据结构
- 插入一个数值
- 查询是否包含某个数值
- 删除某个数值
即:所有的节点都满足 左子树的节点都比自己的小,右子树的节点都比自己的大
//实现二叉搜索树 struct node { int val; node* lch, * rch; }; //插入数值x 返回插入x后的头节点 node* insert(node* p, int x) { //如果 p为空 则申请一个节点返回 if (p == NULL) { node* q = new node; q->val = x; q->lch = q->rch = NULL; return q; } else { //如果x小于当前值 则从当前节点的左孩子节点来查找 if (x < p->val) { p->lch = insert(p->lch, x); } else { p->rch = insert(p->rch, x); } return p; } } //查找 bool find(node* p, int x) { if (p == NULL)return false; if (p->val == x)return true; else if (p->val > x) { return find(p->lch, x); } else { return find(p->rch, x); } } //删除一个数值x 返回删除后二叉搜索树的根节点 //此处需要注意 首先找到x //显然删除x后 需要一个值比x的左边大 并比x的右边小 //需要删除的节点没有左孩子,就把右孩子提上去 //需要删除的节点的左孩子没有右儿子,就把左儿子提上去 //否则把左儿子的子孙中最大的节点提到需要删除的节点上 node* remove(node* p, int x) { if (p == NULL)return NULL; else if (p->val < x) { p->rch = remove(p->rch, x); } else if (p->val > x) { p->lch = remove(p->lch, x); } else { if (p->lch == NULL) { //需要删除的节点没有左孩子,就把右孩子提上去 //即删除该节点 并将本节点的右孩子节点返回 node* r = p->rch; delete p; return r; } else if (p->lch->rch == NULL) { node* r = p -> lch; r->rch = p->rch; delete p; return r; } else { node* temp = p->rch; while (temp->rch->rch != NULL) { temp = temp->rch; } node* r = temp->rch; temp->rch = NULL; r->rch = p->rch; r->lch = p->lch; delete p; return r; } } return p; }
set: 用二叉搜索树维护集合的容器
map:维护键值对的容器
注意:
平衡二叉树
STL中的优先队列为priority_queue,头文件为<queue>,常用操作为:push,top,pop,empty,size。该优先队列默认pop的是最大值,如果想pop最小值(最小堆),则需要指明比较函数:priority_queue<int,vector<int>,greater<int>> que;
STL中实现二叉搜索树的容器有set和map,头文件分别为<set>和<map>。set的常用函数为:insert(重复插入报错),find,erase(删除),start,end(最后一个元素之后),count。允许存放重复键值的容器为multiset和multimap。
4.并查集
查询两个数是否为一组:
直接就沿着树走,看包含这个元素的树的根是谁,如果相同根,则为同一组
合并两个数所在的组:
并查集实现中的注意点: 避免树的退化
//实现并查集 const int MAX_N = 1e5; int parent[MAX_N]; //parent[i]表示i的父亲的编号 int rank[MAX_N];//rank[i]表示i所属树的高度 //初始化n个元素 void init(int n) { for (int i = 0; i < n; i++) { parent[i] = i;//初始化时每个元素的父亲为本身 rank[i] = 0; } } //查询树的根 返回树的根的编号 int find(int x) { if (parent[x] == x) { return x; } else { //return find(parent[x]); //注意每次找到元素的根后 直接将元素连到根上 //所以采用下面的写法 return parent[x] = find(parent[x]); } } //合并x和y所属的集合 void unite(int x, int y) { x = find(x); y = find(y); if (x == y)return;//如果是同一棵树则返回 //合并时将高度小的合并到高度大的树上 if (rank[x] < rank[y]) { parent[x] = y; } else { parent[y] = x; //如果高度相同 默认将y加到了x上 所以x的高度加一下 if (rank[x] == rank[y])rank[x]++; } } //判断x和y是否属于同一个集合 bool same(int x, int y) { return find(x) == find(y); }View Code (实现并查集)
食物链
#include<bits/stdc++.h> using namespace std; const int K_MAX=1e5+5; int n,k,T[K_MAX],x[K_MAX],y[K_MAX]; //准备并查集 const int N_MAX=5e4+5; int par[3*N_MAX],rank111[3*N_MAX]; //初始化 void init(int n) { for(int i=0;i<n;i++) { par[i]=i;//每个元素的根首先是它本身 rank111[i]=0; } } //返回元素的根 int find(int x) { if(par[x]==x)return x; //再往上查根时直接将子节点与根相连 return par[x]=find(par[x]); } //判断两元素是否为一组 bool mySame(int x,int y) { return find(x)==find(y); } //将两个数所在的树合并 void unite(int x,int y) { x=find(x); y=find(y); if(x==y)return; if(rank111[x]<rank111[y]) { par[x]=y; }else{ par[y]=x; if(rank111[x]==rank111[y])rank111[x]++; } } void solve() { init(n*3);//0-n-1 n-2*n-1 2n-3n-1 分别代表 a b c集合 int count=0; //开始读取并处理 for(int i=0;i<k;i++) { //首先处理编号x y 将其改为0-n-1 int xx=x[i]-1; int yy=y[i]-1; //接着判断编号是否越界 if(xx<0||xx>n-1||yy<0||yy>n-1) { count++; continue; } //开始处理消息 if(T[i]==1) { if(mySame(xx,yy+n)||mySame(xx,yy+2*n))//只需要知道xx为特定一组的情况即可 因为都一样啊 { count++; } else{ unite(xx,yy); unite(xx+n,yy+n); unite(xx+2*n,yy+2*n); } } else { if(mySame(xx,yy)||mySame(xx,yy+2*n)) { count++; }else{ unite(xx,yy+n); unite(xx+n,yy+2*n); unite(xx+2*n,yy); } } } printf("%d",count); } int main() { cin>>n>>k; for(int i=0;i<k;i++)cin>>T[i]>>x[i]>>y[i]; solve(); return 0; }
标签:return,int,MAX,else,rch,节点,2.4
来源: https://www.cnblogs.com/lansedemao/p/16513140.html
本站声明:
1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。