ACM-数据结构总结

    声明:搬运自:https://blog.csdn.net/weixin_43093481/article/details/82432254

    但写上了自己的理解,对其补充以及修改,(持续更新……)

一.STL中数据结构常见操作

1. a.assign(b.begin(), b.begin()+3); //b为向量,将b的0~2个元素构成的向量赋给a
2. a.assign(4,2); //是a只含4个元素,且每个元素为2
3. a.back(); //返回a的最后一个元素
4. a.front(); //返回a的第一个元素
5. a[i]; //返回a的第i个元素,当且仅当a[i]存在
6. a.clear(); //清空a中的元素
7. a.empty(); //判断a是否为空,空则返回ture,不空则返回false
8. a.pop_back(); //删除a向量的最后一个元素
9. a.erase(a.begin()+1,a.begin()+3); //删除a中第1个(从第0个算起)到第2个元素
10. a.push_back(5); //在a的最后一个向量后插入一个元素,其值为5
11. a.insert(a.begin()+1,5); //在a的第1个元素(从第0个算起)的位置前插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4
12. a.insert(a.begin()+1,3,5); //在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
13. a.insert(a.begin()+1,b+3,b+6); //b为数组,在a的第1个元素(从第0个算起)的位置插入b的第3个元素到第5个元素(不包括b+6),如b为1,2,3,4,5,9,8 ,插入元素后为1,4,5,9,2,3,4,5,9,8
14. a.size(); //返回a中元素的个数;
15. a.capacity(); //返回a在内存中总共可以容纳的元素个数
16. a.resize(10); //将a的现有元素个数调至10个,多则删,少则补,其值随机
17. a.resize(10,2); //将a的现有元素个数调至10个,多则删,少则补,其值为2
18. a.reserve(100); //将a的容量(capacity)扩充至100,也就是说现在测试a.capacity();的时候返回值是100.这种操作只有在需要给a添加大量数据的时候才 显得有意义,因为这将避免内存多次容量扩充操作(当a的容量不足时电脑会自动扩容,当然这必然降低性能)
19. a.swap(b); //b为向量,将a中的元素和b中的元素进行整体性交换
20. a==b; //b为向量,向量的比较操作还有!=,>=,<=,>,<
21. sort(a.begin(),a.end()); //对a中的从a.begin()(包括它)到a.end()(不包括它)的元素进行从小到大排列
22. reverse(a.begin(),a.end()); //对a中的从a.begin()(包括它)到a.end()(不包括它)的元素倒置,但不排列,如a中元素为1,3,2,4,倒置后为4,2,3,1
23. copy(a.begin(),a.end(),b.begin()+1); //把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置(包括它)开 始复制,覆盖掉原有元素
24. find(a.begin(),a.end(),10); //在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位置

二分查找:(事实上手写也十分方便,适用性广)

       只能对有序数列使用,内部通过二分查找实现

       int pos=lower_bound(a,a+n,m)-a:返回数组a中,0~n里第一个大于等于m的位置

       int pos=upper_bound(a,a+n,m)-a:返回数组a中,0~n里第一个大于m的位置

       lower_bound(a, a+n, m, greater<int>()):加greater<int>()后,lower变小于等于,upper变小

二.栈

       头文件#include<stack>

       建立:stack<int>S;

       入栈:S.push(a);

       取栈顶元素:S.top();

       删除栈顶元素:S.pop();

       取长度:S.size();

       判空:S.empty();

     1.单调栈

    利用单调栈,可以找到从左/右遍历第一个比它小/大的元素的位置.由此也可知道该区间上数的个数O(n) 可统计出a[i]~a[n],以i为起点的单调序列长度,从后向前建立单调栈,常做预处理用一个元素向左遍历的第一个比它小的数的位置就是将它插入单调栈时栈顶元素的值,若栈为空,则说明不存在这么一个数。然后将此元素的下标存入栈,就能类似迭代般地求解后面的元素

       可以用来求寻找他的左面/右面第一个小于/大于他的数或者他的位置 POJ-3250

       给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大 POJ-2559 POJ 3494

       给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大 POJ-2796

       HDU-1506 HDU-5033 

 1 #include <iostream>
 2 #include <stack>
 3 using namespace std;
 4 stack<int>s;
 5 int n;
 6 int ans[N];          //ans[i],表示第i个元素,向左遍历,第一个比它小的元素的位置
 7 void getans(int *a)
 8 {
 9     for(int i=1;i<=n;i++)
10     {
11         while(s.size() && a[s.top()]>=a[i]) s.pop();
12         if(s.empty()) ans[i]=0;
13         else ans[i]=s.top();
14         s.push(i);
15     }
16 }

        或者模拟一个栈会快点??  POJ-3250  个人被stl的速度卡过很多次

        求每头牛被看到的次数之和,将高度小于或等于当前的删掉 求当前栈中储存元素的数量和

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=80005;
 5 int s[maxn],top=0,N=0;
 6 int main()
 7 {
 8     int num=0;
 9     ll ans=0;
10     scanf("%d",&N);
11     while(N--)
12     {
13         scanf("%d",&num);
14         while(top>0&&s[top-1]<=num)top--;
15         ans+=top;
16         s[top++]=num;
17     }
18     printf("%lld
",ans);
19     return 0;

 三.队列

      头文件:#include<queue>

      建立:queue<int>qu;

      入队:qu.push(x);

      取队首:qu.front();

      删除:qu.pop();

      队尾:qu.back();

      判空:qu.empty();

      取长度:qu.size();

       队列的清空 :while(!qu.empty()) qu.pop();

1. 优先队列

      头文件:#include<queue>

      声明:priority_queue<int>qu;

      队首:qu.top();

      默认从大到小(想用从小到大的可以在入队时把他变成负的 出队时取个相反数)

      priority_queue<Type, Container, Functional> Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector

      从小到大的声明:priority_queue< int,vector< int>,greater< int> >qu;

     用 pair 作为优先队列元素

#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main() 
{
    priority_queue<pair<int, int> > a;
    pair<int, int> b(1, 2);
    pair<int, int> c(1, 3);
    pair<int, int> d(2, 5);
    a.push(d);
    a.push(c);
    a.push(b);
    while (!a.empty()) 
    {
        cout << a.top().first << ' ' << a.top().second << '
';
        a.pop();
    }
}

     对结构体使用优先队列:

struct node
{
    int id,c;
    bool operator<(const node&a) const
      { return c<a.c; }
};
priority_queue<node>qu;

2.单调队列

1、维护区间最值;

2、去除冗杂状态;

3、保持队列单调(最大值是单调递减序列,最小值是单调递增序列);

4、最优选择在队首。

整个过程和想法跟单调栈差不多,单调队列也叫滑动窗口,试想,单调栈的过程是维护一个单调递增(暂且考虑为增)的栈,那么他的栈底就是整个序列的最小值,只不过我们取不到栈底,这时候用队列就可以,求出来的是整个序列的最值,但有时我们要求是序列长度为m的子序列的最值,那怎么办??看他的名字,滑动窗口,我们只需要把不属于这个序列的数弹出就好啦,即使他是最小的(但不是我们要求的卵用没有,果断舍掉)

其实,关于求一段区间的最值我们可以用线段树,或者我之前提过的ST表,但好像,单调队列更方便一点QAQ  

 好像一般都用做别的算法(dp) 的优化,纯单调队列很少

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+9;
int a[maxn];
struct node
{
    int x,p;
    node(){}
    node(int xx,int pp)
    {
        x=xx;
        p=pp;
    }
}qu[maxn];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int head=1,tail=0;
    for(int i=1;i<=n;i++)
    {
        while(head<=tail&&qu[tail].x<=a[i]) tail--;//删尾
        qu[++tail]=node(a[i],i);
        while(head<=tail&&i-qu[head].p>=m) head++;//去头
        if(i>=m) printf("%d ",qu[head]);
    }
    return 0;
}

四.向量-动态数组

可作为数组的替代,但效率不如数组

头文件:#include<vector>

建立:vector<int>ve;

加入:ve.push_back(a);

删除:ve.pop_back();

随机访问:ve[i]=x;

返回最后一个:ve.back();

返回第一个:ve.front();

判空:ve.empty()  (空为true)

清空:ve.clear();

段删除:ve.erase(ve.begin()+1,ve.begin()+3);

//删除a中第一个(从第0个算起)到第二个元素,也就是说删除的元素从ve.begin()+1算起(包括它)一直到ve.begin()+3(不包括它)结束

长度:ve.size();

交换:ve.swap(ve_)//将ve与ve_整体交换

比较:va==vb//还可以!= >= > <= <

排序:sort(ve.begin(),ve.end());

倒置:reverse(ve.begin(),ve.end());

查找:find(ve.begin(),ve.end(),x);

 遍历:for(int i=0;i<ve.size();i++)

 

五.链表

stl中默认实现双向链表

头文件:#include<list>

建立:list<int>l;

赋值:l.assign(a);

返回最后一个:l.back();

返回第一个:l.front();

删除:list<int>::iterator it = l.begin(); l.erase(it);

插入:list<int>::iterator it = l.begin(); l.insert(it, 2);//不过迭代器不能it+=9 只能++,这样在中间插岂不是太慢了

删第一个:l.pop_front();

删最后一个:l.pop_back();

在开头加一个:l.push_front();

在结尾加一个:l.push_back();

从list中删除元素:

 判空:ve.empty()  (空为true)

 长度:ve.size();

 遍历:for(it=l.begin();it!=l.end();it++) cout<<*it<<" ";

……

1.链式前向星

即用(数组+结构体)模拟链表

借助一个head数组和一个边的结构体来实现

结构体定义

to 这条边到达的点

cost 边的花费,没有就当他是0

next 与当前起点相同的前一条边的编号(在edge结构体里的下标)

 我们假设边的花费均是1

输入的顺序为

1 2

2 3

3 4

4 5

1 3

1 5

4 1

1.当我们想要得到一条边的终点时,就调用edge[j].to,当我们想得到这个起点连接的其他边时,就可以调用edge[]..next.那么现在的问题就是如何快速求next属性。

2.解决方法就是再定义一个数组head, head[i]表示最近一次输 入的以i为起点的边在edge数组中的下标。

3.初始化的时候head数组都为-1

总的来说就是一个好实现的链表 大概就是这样,画的不好见谅

 代码:

#include <iostream>
#include <string.h>
using namespace std;
int const MAX_M=100000;     //一定要开很大,否则会TLE
int const MAX_N=100000;
struct edge
{
    int to,cost,next;
}e[MAX_M];
int top,head[MAX_N];
void init()
{
    top=0;
    memset(head,-1,sizeof(head));
}
void insert_(int u,int v,int c)
{
    e[top].to=v;
    e[top].cost=c;
    e[top].next=head[u];
    head[u]=top++;
}
void addedge(int u,int v,int c)
{
    insert_(u,v,c);
    insert_(v,u,c);
}
int main()
{
    init()    //初始化,每次必须要有!
    addedge(a,b,c);         //读入边
    for(int i=head[u];i!=-1;i=e[i].next)   //遍历u连向的所有边
    {
        cout<<u<<"->"<<e[i].to<<e[i].next<<e[i].cost;
    }
}

原文地址:https://www.cnblogs.com/YangKun-/p/12500236.html